home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Applications / NIH Image 1.62b11 / src / Analysis.p < prev    next >
Text File  |  1997-05-23  |  75KB  |  2,634 lines

  1. unit Analysis;
  2.  
  3. {Analysis routines used by the NIH Image}
  4.  
  5. interface
  6.  
  7.     uses
  8.         Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
  9.         Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows,
  10.         Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
  11.         globals, Utilities, LeastSquares, Graphics, file1, file2, Ellipse, Lut;
  12.  
  13.  
  14.  
  15.     procedure DoHistogram;
  16.     procedure GetRectHistogram;
  17.     procedure GetHistogram;
  18.     procedure ShowContinuousHistogram;
  19.     procedure ComputeResults;
  20.     procedure FindThresholdingMode;
  21.     procedure Measure;
  22.     procedure UpdateRoiLineWidth;
  23.     procedure DoProfilePlotOptions;
  24.     procedure ShowResults;
  25.     procedure PlotDensityProfile;
  26.     procedure SetScale;
  27.     procedure Calibrate;
  28.     procedure ResetCounter;
  29.     procedure DoMeasurementOptions;
  30.     procedure DoPoints (event: EventRecord);
  31.     procedure FindAngle (event: EventRecord);
  32.     procedure SaveBlankField;
  33.     procedure UndoLastMeasurement (DisplayResults: boolean);
  34.     procedure MarkSelection (count: integer);
  35.     procedure AutoOutline (start: point);
  36.     procedure RedoMeasurement;
  37.     procedure DeleteMeasurement;
  38.     procedure AnalyzeParticles;
  39.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  40.     function isBinaryImage: boolean;
  41.     function DoAPDialog: boolean;
  42.  
  43.  
  44. implementation
  45.  
  46.     const
  47.         UnitsPopUpID = 6;
  48.  
  49.     var
  50.         WandMode: (LUTMode, GrayMapMode, BinaryMode);
  51.         GrayMapThreshold: integer;
  52.         InfoForRedirect: InfoPtr;
  53.         UnitsKind: UnitsType;
  54.  
  55.  
  56.  
  57. {$PUSH}
  58. {$D-}
  59.  
  60.  
  61. procedure DoHistogramOfLine (data: ptr; var histogram: HistogramType; width: LongInt);
  62. {$IFC PowerPC}
  63. VAR
  64.   line:LinePtr;
  65.   i,value:integer;
  66. BEGIN
  67.   line:=LinePtr(data);
  68.   FOR i:=0 TO width-1 DO BEGIN
  69.     value:=line^[i];
  70.     histogram[value]:=histogram[value]+1;
  71.   END;
  72. END;
  73. {$ELSEC}
  74.     {a0=data}
  75.     {a1=histogram}
  76.     {d0=width}
  77.     {d1=pixel value}
  78.     inline
  79.         $4E56, $0000, {  link a6,#0}
  80.         $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  81.         $206E, $000C, {  move.l 12(a6),a0}
  82.         $226E, $0008, {  move.l 8(a6),a1}
  83.         $202E, $0004, {  move.l 4(a6),d0}
  84.         $5380,       {  subq.l #1,d0}
  85.         $4281,       {L clr.l d1}
  86.         $1218,       {  move.b (a0)+,d1}
  87.         $E541,       {  asl.w #2,d1}
  88.         $52B1, $1800, {  addq.l #1,0(a1,d1.l)}
  89.         $51C8, $FFF4, {  dbra d0,L}
  90.         $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  91.         $4E5E,       {  unlk a6}
  92.         $DEFC, $000C; {  add.w #12,sp}
  93. {$ENDC}
  94.  
  95.  
  96.     procedure GetRectHistogram;
  97.         var
  98.             width, i, NumberOfLines: integer;
  99.             offset: LongInt;
  100.             p: ptr;
  101.     begin
  102.         if TooWide then
  103.             exit(GetRectHistogram);
  104.         ShowWatch;
  105.         for i := 0 to 255 do
  106.             Histogram[i] := 0;
  107.         with info^.RoiRect, info^ do begin
  108.                 offset := top * BytesPerRow + left;
  109.                 p := ptr(ord4(PicBaseAddr) + offset);
  110.                 width := right - left;
  111.                 NumberOfLines := bottom - top;
  112.             end;
  113.         if width > 0 then
  114.             for i := 1 to NumberOfLines do begin
  115.                     DoHistogramOfLine(p, histogram, width);
  116.                     p := ptr(ord4(p) + info^.BytesPerRow);
  117.                 end
  118.     end;
  119.  
  120.  
  121.     procedure SetupRedirectedSampling;
  122.         var
  123.             info1, info2, SaveInfo: InfoPtr;
  124.             SameCalibration: boolean;
  125.             i: integer;
  126.     begin
  127.         InfoForRedirect := nil;
  128.         if nPics <> 2 then begin
  129.                 PutError('There must be exactly two image windows open to do redirected sampling.');
  130.                 AnalyzingParticles := false;
  131.                 exit(SetupRedirectedSampling);
  132.             end;
  133.         Info1 := pointer(WindowPeek(PicWindow[1])^.RefCon);
  134.         Info2 := pointer(WindowPeek(PicWindow[2])^.RefCon);
  135.         if not EqualRect(info1^.PicRect, info2^.PicRect) then begin
  136.                 PutError('The image windows must be exactly the same size to do redirected sampling.');
  137.                 AnalyzingParticles := false;
  138.                 exit(SetupRedirectedSampling);
  139.             end;
  140.         if (Info1^.fit <> uncalibrated) or (Info2^.fit <> uncalibrated) then begin
  141.                 SameCalibration := true;
  142.                 if Info1^.fit <> Info2^.fit then
  143.                     SameCalibration := false;
  144.                 if Info1^.nCoefficients <> Info2^.nCoefficients then
  145.                     SameCalibration := false;
  146.                 for i := 1 to info1^.nCoefficients do
  147.                     if Info1^.Coefficient[i] <> Info2^.Coefficient[i] then
  148.                         SameCalibration := false;
  149.                 if not SameCalibration then begin
  150.                         PutError('Both image must be calibrated the same way to do redirected sampling.');
  151.                         AnalyzingParticles := false;
  152.                         exit(SetupRedirectedSampling);
  153.                     end;
  154.             end;
  155.         if info = info1 then
  156.             InfoForRedirect := info2
  157.         else
  158.             InfoForRedirect := info1;
  159.     end;
  160.  
  161.  
  162.     procedure GetHistogram;
  163.         var
  164.             MaskLine, DataLine: LineType;
  165.             width, i, vloc: integer;
  166.             sum, sum2, count, OverFlows: LongInt;
  167.             SaveInfo: InfoPtr;
  168.             value: LongInt;
  169.             trect: rect;
  170.     begin
  171.         if TooWide then
  172.             exit(GetHistogram);
  173.         ShowWatch;
  174.         if RedirectSampling then begin
  175.                 SetupRedirectedSampling;
  176.                 if InfoForRedirect = nil then
  177.                     exit(GetHistogram);
  178.             end
  179.         else
  180.             InfoForRedirect := nil;
  181.         if not SetupMask then
  182.             beep;
  183.         SaveInfo := Info;
  184.         for i := 0 to 255 do
  185.             Histogram[i] := 0;
  186.         if FitEllipse then
  187.             ResetSums;
  188.         trect := info^.RoiRect;
  189.         with trect do begin
  190.                 width := right - left;
  191.                 for vloc := top to bottom - 1 do begin
  192.                         if InfoForRedirect <> nil then
  193.                             Info := InfoForRedirect
  194.                         else
  195.                             Info := SaveInfo;
  196.                         GetLine(left, vloc, width, DataLine);
  197.                         Info := UndoInfo;
  198.                         GetLine(left, vloc, width, MaskLine);
  199.                         if FitEllipse then
  200.                             ComputeSums(vloc - top, width, MaskLine);
  201.                         for i := 0 to width - 1 do
  202.                             if MaskLine[i] = BlackIndex then begin
  203.                                     value := band(DataLine[i],255);
  204.                                     histogram[value] := histogram[value] + 1;
  205.                                 end;
  206.                     end;
  207.             end;
  208.         Info := SaveInfo;
  209.         if not AnalyzingParticles then
  210.             SetupUndo; {Needed for drawing "marching ants".}
  211.     end;
  212.  
  213.  
  214. {$POP}
  215.  
  216.     procedure ComputeResults;
  217.         var
  218.             MaxCount, count, isum, n: LongInt;
  219.             i: integer;
  220.             sum, sum2, ri, tSD, rmode, xc, yc: extended;
  221.             Major, Minor, EllipseAngle, hcenter, vcenter, calValue: extended;
  222.             MinCalibratedValue, MaxCalibratedValue, CalibratedMean: extended;
  223.             IgnoreThresholding: boolean;
  224.             ulength, clength: extended;
  225.     begin
  226.         with info^, results do begin
  227.                 case ThresholdingMode of
  228.                     DensitySlice:  begin
  229.                             MinIndex := SliceStart;
  230.                             MaxIndex := SliceEnd;
  231.                         end;
  232.                     GrayMapThresholding:  begin
  233.                             MinIndex := GrayMapThreshold;
  234.                             MaxIndex := 255;
  235.                         end;
  236.                     BinaryImage:  begin
  237.                             MinIndex := BlackIndex;
  238.                             MaxIndex := BlackIndex;
  239.                         end;
  240.                     NoThresholding:  begin
  241.                             MinIndex := 0;
  242.                             MaxIndex := 255;
  243.                         end;
  244.                 end;
  245.                 IgnoreThresholding := RedirectSampling or (IncludeHoles and (AnalyzingParticles or (CurrentTool = Wand)));
  246.                 if IgnoreThresholding then begin
  247.                         MinIndex := 0;
  248.                         MaxIndex := 255;
  249.                     end;
  250.                 while (histogram[MinIndex] = 0) and (MinIndex < 255) do
  251.                     MinIndex := MinIndex + 1;
  252.                 while (histogram[MaxIndex] = 0) and (MaxIndex > 0) do
  253.                     MaxIndex := MaxIndex - 1;
  254.                 MaxCount := 0;
  255.                 sum := 0.0;
  256.                 isum := 0;
  257.                 sum2 := 0.0;
  258.                 n := 0;
  259.                 minCalibratedValue := 10e100;
  260.                 maxCalibratedValue := -10e100;
  261.                 rmode := 0.0;
  262.                 imode := 0;
  263.                 for i := MinIndex to MaxIndex do begin
  264.                         calValue := cvalue[i];
  265.                         count := histogram[i];
  266.                         sum := sum + count * calValue;
  267.                         isum := isum + count * i;
  268.                         ri := i;
  269.                         sum2 := sum2 + sqr(calValue) * count;
  270.                         n := n + count;
  271.                         if count > MaxCount then begin
  272.                                 MaxCount := count;
  273.                                 rmode := cvalue[i];
  274.                                 imode := i
  275.                             end;
  276.                         if calValue < minCalibratedValue then
  277.                             minCalibratedValue := calValue;
  278.                         if calValue > maxCalibratedValue then
  279.                             maxCalibratedValue := calValue;
  280.                     end;
  281.                 if ContinuousHistoGram then
  282.                     exit(ComputeResults);
  283.                 if n = 0 then begin
  284.                         minCalibratedValue := 0.0;
  285.                         maxCalibratedValue := 0.0;
  286.                     end;
  287.                 if n > 0 then begin
  288.                         CalibratedMean := sum / n;
  289.                         UncalibratedMean := isum / n
  290.                     end
  291.                 else begin
  292.                         CalibratedMean := 0.0;
  293.                         UncalibratedMean := 0.0
  294.                     end;
  295.                 IncrementCounter;
  296.                 mean^[mCount] := CalibratedMean;
  297.                 mMin^[mCount] := minCalibratedValue;
  298.                 mMax^[mCount] := maxCalibratedValue;
  299.                 if mCount <= MaxStandards then
  300.                     umean[mCount] := UncalibratedMean;
  301.                 if n > 0 then begin
  302.                         tSD := (n * Sum2 - sqr(sum)) / n;
  303.                         if tSD > 0.0 then
  304.                             tSD := sqrt(tSD / (n - 1.0))
  305.                         else
  306.                             tSD := 0.0
  307.                     end
  308.                 else
  309.                     tSD := 0.0;
  310.                 sd^[mCount] := tSD;
  311.                 PixelCount^[mCount] := n;
  312.                 ulength := 0.0;
  313.                 clength := 0.0;
  314.                 with RoiRect do
  315.                     case RoiType of
  316.                         RectRoi:  begin
  317.                                 uLength := ((right - left) + (bottom - top)) * 2.0;
  318.                                 cLength := uLength;
  319.                                 if SpatiallyCalibrated then
  320.                                     cLength := ((right - left) / xScale + (bottom - top) / yScale) * 2.0;
  321.                             end;
  322.                         OvalRoi:  begin
  323.                                 uLength := pi * ((right - left) + (bottom - top)) / 2.0;
  324.                                 cLength := uLength;
  325.                                 if SpatiallyCalibrated then
  326.                                     cLength := pi * ((right - left) / xScale + (bottom - top) / yScale) / 2.0;
  327.                             end;
  328.                         LineRoi, SegLineRoi, FreeLineRoi:  begin
  329.                                 GetLengthOrPerimeter(ulength, clength);
  330.                                 nLengths := nLengths + 1;
  331.                             end;
  332.                         PolygonRoi, FreehandRoi, TracedRoi: 
  333.                             if (LengthM in Measurements) or (nLengths > 0) or WandAdjustAreas then
  334.                                 GetLengthOrPerimeter(ulength, clength);
  335.                         otherwise
  336.                     end;
  337.                 if SpatiallyCalibrated then
  338.                     plength^[mCount] := cLength
  339.                 else
  340.                     plength^[mcount] := uLength;
  341.                 if SpatiallyCalibrated then
  342.                     mArea^[mCount] := n / (xScale * yScale)
  343.                 else
  344.                     mArea^[mCount] := n;
  345.                 mode^[mCount] := rmode;
  346.                 if FitEllipse then begin
  347.                     GetEllipseParam(Major, Minor, EllipseAngle, xc, yc);
  348.                     if InvertYCoordinates then
  349.                         yc := PicRect.bottom - yc;
  350.                     if SpatiallyCalibrated then begin
  351.                             Major := Major / xScale;
  352.                             Minor := Minor / xScale;
  353.                             xc := xc / xScale;
  354.                             yc := yc / yScale;
  355.                         end;
  356.                     MajorAxis^[mCount] := Major * 2.0;
  357.                     MinorAxis^[mCount] := Minor * 2.0;
  358.                     orientation^[mCount] := EllipseAngle;
  359.                     xcenter^[mCount] := xc;
  360.                     ycenter^[mCount] := yc;
  361.                 end else begin
  362.                     MajorAxis^[mCount] := 0.0;
  363.                     MinorAxis^[mCount] := 0.0;
  364.                     orientation^[mCount] := 0.0;
  365.                     with RoiRect do begin
  366.                         xc := left + (right - left) / 2.0;
  367.                         yc := top + (bottom - top) / 2.0;
  368.                         if InvertYCoordinates then
  369.                             yc := PicRect.bottom - yc;
  370.                         if SpatiallyCalibrated then begin
  371.                                 xc := xc / xScale;
  372.                                 yc := yc / yScale;
  373.                             end;
  374.                         xcenter^[mCount] := xc;
  375.                         ycenter^[mCount] := yc;
  376.                     end;
  377.                 end;
  378.             end; {with}
  379.         measuring := true;
  380.         InfoMessage := '';
  381.     end;
  382.  
  383.  
  384. {$PUSH}
  385. {$D-}
  386.  
  387.  
  388.     procedure FindThresholdingMode;
  389.     begin
  390.         with info^ do begin
  391.                 if DensitySlicing then
  392.                     ThresholdingMode := DensitySlice
  393.                 else if thresholding then begin
  394.                         ThresholdingMode := GrayMapThresholding;
  395.                         GrayMapThreshold := ColorStart;
  396.                         if GrayMapThreshold < 0 then
  397.                             GrayMapThreshold := 0;
  398.                         if GrayMapThreshold > 255 then
  399.                             GrayMapThreshold := 255;
  400.                     end
  401.                 else if BinaryPic then
  402.                     ThresholdingMode := BinaryImage
  403.                 else
  404.                     ThresholdingMode := NoThresholding;
  405.             end;
  406.     end;
  407.  
  408.  
  409.     procedure Measure;
  410.         var
  411.             AutoSelectAll: boolean;
  412.             SaveN: integer;
  413.     begin
  414.         if NotInBounds then
  415.             exit(Measure);
  416.         with info^ do begin
  417.                 FindThresholdingMode;
  418.                 if ThresholdingMode = BinaryImage then
  419.                     ThresholdingMode := NoThresholding;
  420.                 AutoSelectAll := not RoiShowing;
  421.                 if AutoSelectAll then
  422.                     SelectAll(false);
  423.                 if (RoiType = RectRoi) and (not RedirectSampling) then
  424.                     GetRectHistogram
  425.                 else
  426.                     GetHistogram;
  427.                 if MeasurementToRedo > 0 then begin
  428.                         SaveN := mCount;
  429.                         mCount := MeasurementToRedo - 1;
  430.                         ComputeResults;
  431.                         ShowInfo;
  432.                         mCount := SaveN;
  433.                         MeasurementToRedo := 0;
  434.                         UpdateList;
  435.                     end
  436.                 else begin
  437.                         ComputeResults;
  438.                         ShowInfo;
  439.                         AppendResults;
  440.                         if RoiType = LineRoi then
  441.                             if nLengths = 1 then
  442.                                 if not (LengthM in Measurements) then
  443.                                     UpdateList;
  444.                     end;
  445.                 RoiShowing := true;
  446.                 WhatToUndo := UndoMeasurement;
  447.                 if AutoSelectAll then
  448.                     KillRoi;
  449.                 UpdateScreen(OldRoiRect);
  450.             end;
  451.     end;
  452.  
  453.  
  454.     procedure ShowHistogram;
  455.         var
  456.             htop: integer;
  457.             tport: GrafPtr;
  458.             hrect, prect, srect: rect;
  459.             FirstTime: boolean;
  460.     begin
  461.         GetPort(tPort);
  462.         FirstTime := HistoWindow = nil;
  463.         if FirstTime then begin
  464.                 htop := ScreenHeight - hheight - 10;
  465.                 SetRect(HistoRect, hleft, htop, hleft + hwidth, htop + hheight);
  466.                 HistoWindow := NewWindow(nil, HistoRect, 'Histogram', true, NoGrowDocProc, nil, true, 0);
  467.                 WindowPeek(HistoWindow)^.WindowKind := HistoKind;
  468.             end;
  469.         if FirstTime or (VideoControl = nil) then
  470.             SelectWindow(HistoWindow);
  471.         SetPort(HistoWindow);
  472.         InvalRect(HistoWindow^.PortRect);
  473.         SetPort(tPort);
  474.     end;
  475.  
  476.  
  477.     procedure ShowContinuousHistogram;
  478.         const
  479.             skip = 10;
  480.         var
  481.             i, NumberOfLines: integer;
  482.             offset: LongInt;
  483.             p: ptr;
  484.     begin
  485.         with info^ do
  486.             if (FrameGrabber = QTvdig) and (PictureType = FrameGrabberType) then
  487.                 CopyOffscreen(fgPixMap, osPort^.portPixMap, PicRect, PicRect);
  488.         for i := 0 to 255 do
  489.             Histogram[i] := 0;
  490.         p := ptr(ptr(fgSlotBase));
  491.         NumberOfLines := ((fgHeight) div skip) - 1;
  492.         offset := fgRowBytes * skip;
  493.         for i := 1 to NumberOfLines do begin
  494.                 DoHistogramOfLine(p, histogram, fgWidth);
  495.                 p := ptr(ord4(p) + offset);
  496.             end;
  497.         ThresholdingMode := NoThresholding;
  498.         HistogramSliceStart := 0;
  499.         HistogramSliceEnd := 255;
  500.         ComputeResults;
  501.         ShowHistogram;
  502.     end;
  503.  
  504.  
  505.     procedure DoHistogram;
  506.         var
  507.             AutoSelectAll: boolean;
  508.     begin
  509.         if NotInBounds then
  510.             exit(DoHistogram);
  511.         if digitizing then begin
  512.                 if ContinuousHistogram then
  513.                     ContinuousHistogram := false
  514.                 else begin
  515.                         ContinuousHistogram := true;
  516.                         if info <> NoInfo then
  517.                             with info^ do begin
  518.                                     RoiType := NoRoi;
  519.                                     RoiRect := SrcRect;
  520.                                 end;
  521.                     end;
  522.                 exit(DoHistogram)
  523.             end;
  524.         AutoSelectAll := not info^.RoiShowing;
  525.         if AutoSelectAll then
  526.             SelectAll(false);
  527.         if (info^.RoiType = RectRoi) and (not RedirectSampling) then
  528.             GetRectHistogram
  529.         else
  530.             GetHistogram;
  531.         ThresholdingMode := NoThresholding;
  532.         ComputeResults;
  533.         ShowCount := false;
  534.         ShowInfo;
  535.         ShowCount := true;
  536.         FindThresholdingMode;
  537.         case ThresholdingMode of
  538.             DensitySlice:  begin
  539.                     HistogramSliceStart := SliceStart;
  540.                     HistogramSliceEnd := SliceEnd;
  541.                 end;
  542.             GrayMapThresholding:  begin
  543.                     HistogramSliceStart := GrayMapThreshold;
  544.                     HistogramSliceEnd := 255;
  545.                 end;
  546.             BinaryImage, NoThresholding:  begin
  547.                     HistogramSliceStart := 0;
  548.                     HistogramSliceEnd := 255;
  549.                 end;
  550.         end;
  551.         ShowHistogram;
  552.         UndoLastMeasurement(false);
  553.         WhatToUndo := NothingToUndo;
  554.         if AutoSelectAll then
  555.             KillRoi;
  556.     end;
  557.  
  558.  
  559. {$POP}
  560.  
  561.     procedure PlotDensityProfile;
  562.         var
  563.             hloc, vloc, value, width, height, i: integer;
  564.             aLine: LineType;
  565.             sum: array[0..MaxLine] of real;
  566.             start, p1, p2: point;
  567.     begin
  568.         with info^ do
  569.             if RoiShowing then
  570.                 case RoiType of
  571.                     LineRoi:  begin
  572.                             PlotLineProfile;
  573.                             exit(PlotDensityProfile);
  574.                         end;
  575.                     FreeLineRoi, SegLineRoi, PolygonRoi, FreehandRoi, TracedRoi:  begin
  576.                             PlotArbitraryLine;
  577.                             exit(PlotDensityProfile);
  578.                         end;
  579.                 end; {case}
  580.         if NoSelection or NotRectangular or NotInBounds then
  581.             exit(PlotDensityProfile);
  582.         ShowWatch;
  583.         with info^.RoiRect do begin
  584.                 width := right - left;
  585.                 height := bottom - top;
  586.                 start.h := left;
  587.                 start.v := bottom;
  588.                 if (width >= height) or (OptionKeyWasDown) then begin
  589.             {Column Average Plot}
  590.                         if width > MaxLine then begin
  591.                             PlotTooLongMsg;
  592.                             exit(PlotDensityProfile);
  593.                         end;
  594.                         for i := 0 to width - 1 do
  595.                             sum[i] := 0.0;
  596.                         for vloc := top to bottom - 1 do begin
  597.                                 GetLine(left, vloc, width, aLine);
  598.                                 for i := 0 to width - 1 do
  599.                                     sum[i] := sum[i] + cvalue[aLine[i]];
  600.                             end;
  601.                         for i := 0 to width - 1 do
  602.                             PlotData^[i] := sum[i] / height;
  603.                         PlotCount := width;
  604.                         PlotAvg := height;
  605.                         PlotStart.h := left;
  606.                         PlotStart.v := top + (bottom - top) div 2;
  607.                         PlotAngle := 0.0;
  608.                         ComputePlotMinAndMax;
  609.                         if ShowPlot then
  610.                             SetupPlot(start, false);
  611.                     end
  612.                 else begin
  613.            {Row Average Plot}
  614.                         if height > MaxLine then begin
  615.                             PlotTooLongMsg;
  616.                             exit(PlotDensityProfile);
  617.                         end;
  618.                         for i := 0 to height - 1 do
  619.                             sum[i] := 0.0;
  620.                         for hloc := left to right - 1 do begin
  621.                                 GetColumn(hloc, top, height, aLine);
  622.                                 for i := 0 to height - 1 do
  623.                                     sum[i] := sum[i] + cValue[aLine[i]];
  624.                             end;
  625.                         for i := 0 to height - 1 do
  626.                             PlotData^[i] := sum[i] / width;
  627.                         PlotCount := height;
  628.                         PlotAvg := width;
  629.                         PlotStart.h := left + (right - left) div 2;
  630.                         PlotStart.v := top;
  631.                         PlotAngle := 270.0;
  632.                         ComputePlotMinAndMax;
  633.                         if ShowPlot then
  634.                             SetupPlot(start, true);
  635.                     end;
  636.             end; {with}
  637.     end;
  638.  
  639.  
  640.     procedure SetScaleUProc (d: DialogPtr; item: integer);
  641.      {User proc for Set Scale dialog box}
  642.         var
  643.             str: str255;
  644.             VersInfo: str255;
  645.             r: rect;
  646.     begin
  647.         SetPort(d);
  648.         GetDItemRect(d, item, r);
  649.         DrawDropBox(r);
  650.         GetMenuItemText(UnitsMenuH, ord(UnitsKind) + 1, str);
  651.         DrawPopUpText(str, r);
  652.     end;
  653.  
  654.  
  655.     procedure SetScale;
  656.         const
  657.             MeasuredDistanceID = 3;
  658.             KnownDistanceID = 4;
  659.             AspectRatioID = 5;
  660.             ScaleID = 7;
  661.             UnitsTextID = 8;
  662.         var
  663.             mylog: DialogPtr;
  664.             item, i: integer;
  665.             SaveUnitsKind, OldUnitsKind, MenuUnitsKind: UnitsType;
  666.             KnownDistance, MeasuredDistance, SaveScale, TempScale, CalibratedDistance: extended;
  667.             UnitsPerCM, OldUnitsPerCM, SaveRawScale, SaveAspectRatio: extended;
  668.             ignore, MenuItem: integer;
  669.             str: str255;
  670.             SaveUnits: UnitType;
  671.             isLineSelection: boolean;
  672.             ulength, clength: extended;
  673.             r: rect;
  674.     begin
  675.         if SetScaleUserProc=nil
  676.             then SetScaleUserProc:=NewRoutineDescriptor(@SetScaleUProc, uppUserItemProcInfo, GetCurrentISA);
  677.         with info^ do begin
  678.                 if (not RoiShowing) and (CurrentTool = LineTool) and (NoInfo^.roiType = LineRoi) then
  679.                     RestoreRoi;
  680.                 isLineSelection := RoiShowing and (RoiType = LineRoi);
  681.                 InitCursor;
  682.                 if isLineSelection then begin
  683.                         GetLengthOrPerimeter(ulength, clength);
  684.                         MeasuredDistance := ulength;
  685.                     end
  686.                 else
  687.                     MeasuredDistance := 0.0;
  688.                 if not SpatiallyCalibrated then
  689.                     xUnit := 'pixel';
  690.                 GetUnitsKind(UnitsKind, UnitsPerCM);
  691.                 SaveUnits := xUnit;
  692.                 SaveUnitsKind := UnitsKind;
  693.                 SaveScale := xScale;
  694.                 SaveAspectRatio := PixelAspectRatio;
  695.                 KnownDistance := 0.0;
  696.                 mylog := GetNewDialog(10, nil, pointer(-1));
  697.                 SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  698.                 SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  699.                 SelectdialogItemText(MyLog, KnownDistanceID, 0, 32767);
  700.                 SetDReal(MyLog, AspectRatioID, PixelAspectRatio, 4);
  701.                 SetUProc(myLog, UnitsPopupID, handle(SetScaleUserProc));
  702.                 if UnitsKind = pixels then
  703.                     TempScale := 1.0
  704.                 else
  705.                     TempScale := xScale;
  706.                 if trunc(TempScale) = TempScale then
  707.                     SetDReal(MyLog, ScaleID, TempScale, 0)
  708.                 else
  709.                     SetDReal(MyLog, ScaleID, TempScale, 5);
  710.                 SetDString(MyLog, UnitsTextID, xUnit);
  711.                 setport(myLog);
  712.                 repeat
  713.                     ModalDialog(nil, item);
  714.                     if item = MeasuredDistanceID then
  715.                         MeasuredDistance := GetDReal(MyLog, MeasuredDistanceID);
  716.                     if item = KnownDistanceID then
  717.                         KnownDistance := GetDReal(MyLog, KnownDistanceID);
  718.                     if item = ScaleID then begin
  719.                             MeasuredDistance := GetDReal(MyLog, ScaleID);
  720.                             KnownDistance := 1;
  721.                             SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  722.                             SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  723.                         end;
  724.                     if item = AspectRatioID then begin
  725.                             PixelAspectRatio := GetDReal(MyLog, AspectRatioID);
  726.                             if PixelAspectRatio <= 0.0 then begin
  727.                                     beep;
  728.                                     PixelAspectRatio := 1.0;
  729.                                 end;
  730.                         end;
  731.                     if item = UnitsPopUpID then begin
  732.                             OldUnitsKind := UnitsKind;
  733.                             OldUnitsPerCM := UnitsPerCM;
  734.                             GetDItemRect(myLog, item, r);
  735.                             InvertRect(r);
  736.                             MenuItem := PopUpMenu(UnitsMenuH, r.left, r.top, ord(UnitsKind) + 1);
  737.                             InvertRect(r);
  738.                             GetMenuItemText(UnitsMenuH, MenuItem, str);
  739.                             DrawPopUpText(str, r);
  740.                             UnitsKind := UnitsType(MenuItem - 1);
  741.                             GetXUnits(UnitsKind);
  742.                             if (UnitsType(MenuItem - 1) = OtherUnits) and (OldUnitsKind <> OtherUnits) then
  743.                                 xUnit := 'unit';
  744.                             SetDString(MyLog, UnitsTextID, xUnit);
  745.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  746.                             if (UnitsPerCM <> OldUnitsPerCM) and (UnitsPerCM <> 0.0) and (OldUnitsPerCM <> 0.0) then
  747.                                     xScale := xScale * (OldUnitsPerCM / UnitsPerCM);
  748.                             if UnitsKind = Pixels then
  749.                                 KnownDistance := 0.0;
  750.                         end;
  751.                     if (item = KnownDistanceID) or (item = MeasuredDistanceID) or (item = ScaleID) then
  752.                         if (UnitsKind = Pixels) and (item <> cancel) then
  753.                             PutError('Please select a measurent unit (not pixels) before setting or changing the scale.')
  754.                         else begin
  755.                                 if (MeasuredDistance > 0.0) and (KnownDistance > 0.0) then
  756.                                         xScale := MeasuredDistance / KnownDistance;
  757.                             end;
  758.                     if UnitsKind = pixels then
  759.                         TempScale := 1.0
  760.                     else
  761.                         TempScale := xScale;
  762.                     if item <> ScaleID then begin
  763.                             if (trunc(TempScale) = TempScale) or (TempScale >= 10000.0) then
  764.                                 SetDReal(MyLog, ScaleID, TempScale, 0)
  765.                             else if TempScale < 0.01 then
  766.                                 SetDReal(MyLog, ScaleID, TempScale, 6)
  767.                             else
  768.                                 SetDReal(MyLog, ScaleID, TempScale, 3);
  769.                         end;
  770.                     if item = UnitsTextID then begin
  771.                             str := GetDString(myLog, item);
  772.                             TruncateString(str, maxUnit);
  773.                             xUnit := str;
  774.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  775.                             GetDItemRect(myLog, UnitsPopUpID, r);
  776.                             InvalRect(r);
  777.                         end;
  778.                 until (item = ok) or (item = cancel);
  779.                 DisposeDialog(mylog);
  780.                 if item = cancel then begin
  781.                         xUnit := SaveUnits;
  782.                         UnitsKind := SaveUnitsKind;
  783.                         xScale := SaveScale;
  784.                         PixelAspectRatio := SaveAspectRatio;
  785.                     end
  786.                 else
  787.                     Changes := true;
  788.                 SpatiallyCalibrated := (xScale <> 0.0) and (xUnit <> 'pixel');
  789.                 if SpatiallyCalibrated then
  790.                     yScale := xScale / PixelAspectRatio
  791.                 else begin
  792.                     UnitsKind := Pixels;
  793.                     UnitsPerCm := 0.0;
  794.                     PixelAspectRatio:=1.0;
  795.                 end;
  796.                 UpdateTitleBar;
  797.                 if item<>cancel then begin
  798.                     NoInfo^.SpatiallyCalibrated:=SpatiallyCalibrated;
  799.                     NoInfo^.xUnit := xUnit;
  800.                     NoInfo^.xScale := xScale;
  801.                     NoInfo^.PixelAspectRatio := PixelAspectRatio;
  802.                 end;
  803.             end; {with info^}
  804.     end;
  805.  
  806.  
  807. {$PUSH}
  808. {$D-}
  809.  
  810.  
  811.     procedure SetupCalibrationPlot;
  812.         const
  813.             hrange = 1024;
  814.             hmax = 1023;
  815.             vrange = 600;
  816.             vmax = 599;
  817.             SymbolSize = 11;
  818.         var
  819.             fRect, tRect: rect;
  820.             svalue, range, hscale, vscale, MinV, MaxV: extended;
  821.             tPort: GrafPtr;
  822.             i, hloc, vloc: integer;
  823.             SaveClipRegion: RgnHandle;
  824.             pt: point;
  825.     begin
  826.         PlotLeftMargin := 60;
  827.         PlotTopMargin := 15;
  828.         PlotBottomMargin := 30;
  829.         PlotRightMargin := 100;
  830.         MinV := minCValue;
  831.         MaxV := maxCValue;
  832.         for i := 1 to nStandards do begin
  833.                 svalue := StandardValues[i];
  834.                 if svalue < MinV then
  835.                     MinV := svalue;
  836.                 if svalue > MaxV then
  837.                     MaxV := svalue;
  838.             end;
  839.         range := MaxV - MinV;
  840.         PlotWidth := hrange div 3 + PlotLeftMargin + PlotRightMargin;
  841.         PlotHeight := vrange div 3 + PlotTopMargin + PlotBottomMargin;
  842.         PlotLeft := 64;
  843.         PlotTop := 64;
  844.         for i := 0 to 255 do
  845.             PlotData^[i] := cvalue[i];
  846.         PlotAvg := 1;
  847.         PlotCount := 256;
  848.         MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  849.         if PlotWindow = nil then
  850.             exit(SetupCalibrationPlot);
  851.         WindowPeek(PlotWindow)^.WindowKind := CalibrationPlotKind;
  852.         SetRect(fRect, -SymbolSize, -SymbolSize, hmax + SymbolSize, vmax + SymbolSize);
  853.         GetPort(tPort);
  854.         SetPort(PlotWindow);
  855.         SaveClipRegion := PlotWindow^.ClipRgn;
  856.         RectRgn(PlotWindow^.ClipRgn, fRect);
  857.         hscale := 256.0 / round(hrange);
  858.         vscale := range / vrange;
  859.         PlotPICT := OpenPicture(fRect);
  860.         for i := 1 to nStandards do begin
  861.                 hloc := round(umean[i] / hscale);
  862.                 vloc := vmax - round((StandardValues[i] - minCValue) / vscale);
  863.                 SetRect(tRect, hloc - SymbolSize, vloc - SymbolSize, hloc + SymbolSize, vloc + SymbolSize);
  864.                 FrameOval(tRect);
  865.             end;
  866.         MoveTo(0, vmax - round((cvalue[0] - minCValue) / vscale));
  867.         for i := 1 to 255 do begin
  868.                 hloc := round(i / hscale);
  869.                 vloc := vmax - round((cvalue[i] - minCValue) / vscale);
  870.                 LineTo(hloc, vloc);
  871.             end;
  872.         ClosePicture;
  873.         PlotWindow^.ClipRgn := SaveClipRegion;
  874.         InvalRect(PlotWindow^.PortRect);
  875.         SetPort(tPort);
  876.         SelectWindow(PlotWindow);
  877.     end;
  878.  
  879.  
  880.     procedure DoCurveFitting;
  881.         var
  882.             i: integer;
  883.             XData, YData, YFit, Residuals, TempData: ColumnVector;
  884.             Variance: extended;
  885.             SumResidualsSqr, SumStandards, mean, SumMeanDiffSqr, DegreesOfFreedom: extended;
  886.             str1, str2: str255;
  887.     begin
  888.         with info^ do begin
  889.                 ShowWatch;
  890.                 if fit = RodbardFit then { need to reverse x and y to fit Rodbard equation }
  891.                     for i := 1 to nStandards do begin
  892.                             XData[i] := StandardValues[i];
  893.                             YData[i] := umean[i];
  894.                         end
  895.                 else
  896.                     for i := 1 to nStandards do begin
  897.                             XData[i] := umean[i];
  898.                             YData[i] := StandardValues[i];
  899.                         end;
  900.                 case fit of
  901.                     StraightLine: 
  902.                         nCoefficients := 2;
  903.                     Poly2: 
  904.                         nCoefficients := 3;
  905.                     Poly3: 
  906.                         nCoefficients := 4;
  907.                     Poly4: 
  908.                         nCoefficients := 5;
  909.                     Poly5: 
  910.                         nCoefficients := 6;
  911.                     ExpoFit: 
  912.                         nCoefficients := 2;
  913.                     PowerFit: 
  914.                         nCoefficients := 2;
  915.                     LogFit: 
  916.                         nCoefficients := 2;
  917.                     RodbardFit: 
  918.                         nCoefficients := 4;
  919.                 end;
  920.                 DegreesOfFreedom := nStandards - nCoefficients;
  921.                 if DegreesOfFreedom < 0 then begin
  922.                         FitGoodness := 0.0;
  923.                         NumToString(nCoefficients, str1);
  924.                         case fit of
  925.                             StraightLine: 
  926.                                 str2 := 'straight line';
  927.                             Poly2: 
  928.                                 str2 := '2nd degree polynomial';
  929.                             Poly3: 
  930.                                 str2 := '3rd degree polynomial';
  931.                             Poly4: 
  932.                                 str2 := '4th degree polynomial';
  933.                             Poly5: 
  934.                                 str2 := '5th degree polynomial';
  935.                             ExpoFit: 
  936.                                 str2 := 'exponential';
  937.                             PowerFit: 
  938.                                 str2 := 'power';
  939.                             LogFit: 
  940.                                 str2 := 'log';
  941.                             RodbardFit: 
  942.                                 str2 := 'Rodbard';
  943.                         end;
  944.                         str2 := concat(' standards to do ', str2, ' fitting.');
  945.                         PutError(concat('You need at least ', str1, str2));
  946.                         AbortMacro;
  947.                         fit:=Uncalibrated;
  948.                         exit(DoCurveFitting)
  949.                     end;
  950.                 DoSimplexFit(nStandards, nCoefficients, XData, YData, Coefficient, residuals);
  951.                 ZeroClip := true;
  952.                 for i := 1 to nStandards do
  953.                     if ydata[i] < 0.0 then
  954.                         ZeroClip := false;
  955.                 GenerateValues;
  956.                 SumResidualsSqr := 0.0;
  957.                 SumStandards := 0.0;
  958.                 if fit = RodbardFit then
  959.                     for i := 1 to nStandards do begin
  960.                             tempdata[i] := StandardValues[i];
  961.                             StandardValues[i] := umean[i];
  962.                         end;
  963.                 for i := 1 to nStandards do begin
  964.                         SumResidualsSqr := SumResidualsSqr + sqr(residuals[i]);
  965.                         SumStandards := SumStandards + StandardValues[i];
  966.                     end;
  967.                 FitSD := Sqrt(SumResidualsSqr / nStandards);
  968.                 mean := SumStandards / nStandards;
  969.                 SumMeanDiffSqr := 0.0;
  970.                 for i := 1 to nStandards do
  971.                     SumMeanDiffSqr := SumMeanDiffSqr + sqr(StandardValues[i] - Mean);
  972.                 if (SumMeanDiffSqr > 0.0) and (DegreesOfFreedom <> 0) then
  973.                     FitGoodness := 1 - (SumResidualsSqr / DegreesOfFreedom) * ((nStandards - 1) / SumMeanDiffSqr)
  974.                 else
  975.                     FitGoodness := 1.0;
  976.                 if fit = RodbardFit then
  977.                     for i := 1 to nStandards do
  978.                         StandardValues[i] := tempdata[i];
  979.             end;
  980.         info^.changes := true;
  981.     end;
  982.  
  983.  
  984.     procedure GetStandardsFromFile (mylog: DialogPtr; FirstLevelID, FirstStandardID: integer);
  985.         var
  986.             fname, str: str255;
  987.             RefNum, i, nColumns, nValues: integer;
  988.             rLine: RealLine;
  989.     begin
  990.         RefNum := 0;
  991.         if not GetTextFile(fname, RefNum) then
  992.             exit(GetStandardsFromFile);
  993.         InitTextInput(fname, RefNum);
  994.         GetLineFromText(rLine, nValues);
  995.         if nValues = 1 then
  996.             nColumns := 1
  997.         else
  998.             nColumns := 2;
  999.         if (nStandards = 0) and (nColumns = 2) then begin
  1000.                 i := 0;
  1001.                 repeat
  1002.                     i := i + 1;
  1003.                     if i > MaxStandards then
  1004.                         i := MaxStandards;
  1005.                     umean[i] := rLine[1];
  1006.                     SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  1007.                     StandardValues[i] := rLine[2];
  1008.                     SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  1009.                     GetLineFromText(rLine, nValues);
  1010.                 until nValues = 0;
  1011.                 nStandards := i;
  1012.                 mCount := nStandards;
  1013.                 for i := 1 to mCount do begin
  1014.                         ClearResults(i);
  1015.                         mean^[i] := umean[i];
  1016.                     end;
  1017.             end
  1018.         else
  1019.             for i := 1 to nStandards do begin
  1020.                     if nValues = nColumns then begin
  1021.                             StandardValues[i] := rLine[nColumns];
  1022.                             SetDReal(MyLog, FirststandardID + i - 1, StandardValues[i], 3);
  1023.                         end;
  1024.                     GetLineFromText(rLine, nValues);
  1025.                 end;
  1026.         InitCursor;
  1027.     end;
  1028.  
  1029.  
  1030.     procedure SaveStandardsToFile (nStandards: integer);
  1031.         var
  1032.             where: Point;
  1033.             reply: SFReply;
  1034.             i: integer;
  1035.             OptionKeyWasDown: boolean;
  1036.     begin
  1037.         OptionKeyWasDown := OptionKeyDown;
  1038.         where.v := 60;
  1039.         where.h := 100;
  1040.         SFPutFile(Where, 'Save Calibration as?', 'Standards', nil, reply);
  1041.         if reply.good then begin
  1042.                 TextBufSize := 0;
  1043.                 for i := 1 to nStandards do begin
  1044.                         PutReal(umean[i], 1, 3);
  1045.                         PutChar(tab);
  1046.                         if StandardValues[i] >= 100.0 then
  1047.                             PutReal(StandardValues[i], 1, 3)
  1048.                         else
  1049.                             PutReal(StandardValues[i], 1, 5);
  1050.                         if i <> nStandards then
  1051.                             PutChar(cr);
  1052.                     end;
  1053.                 with reply do
  1054.                     SaveAsText(fname, vRefNum);
  1055.             end;
  1056.         InitCursor;
  1057.     end;
  1058.  
  1059.  
  1060.     procedure SetupUncalibratedOD;
  1061.         var
  1062.             i: integer;
  1063.     begin
  1064.         with info^ do begin
  1065.                 ZeroClip := false;
  1066.                 nCoefficients := 0;
  1067.                 for i := 1 to 6 do
  1068.                     Coefficient[i] := 1.0;
  1069.                 fit := UncalibratedOD;
  1070.                 GenerateValues;
  1071.                 UnitOfMeasure := 'U. OD';
  1072.                 nStandards := 0;
  1073.                 nKnownValues := 0;
  1074.             end;
  1075.     end;
  1076.  
  1077.  
  1078.     function InvertOD (var temp: StandardsArray): boolean;
  1079.         var
  1080.             i: integer;
  1081.     begin
  1082.         for i := 1 to nStandards do
  1083.             if (StandardValues[i] < 0.000009) or (StandardValues[i] > 4.64) then begin
  1084.                     PutError('Known OD Values must be in the range 0.00001 to 4.62.');
  1085.                     InvertOD := false;
  1086.                     exit(InvertOD);
  1087.                 end;
  1088.         for i := 1 to nStandards do  {temp[i] := -log10(1.000 - exp10(-StandardValues[i]));}
  1089.             temp[i] := -0.434294481 * ln(1.000 - exp(-2.302585093 * StandardValues[i]));
  1090.         InvertOD := true;
  1091.     end;
  1092.  
  1093.  
  1094.     function DoCalibrateDialog:boolean;
  1095.     const
  1096.         FirstLevelID = 3;
  1097.         FirstStandardID = 23;
  1098.         FirstFitID = 63;
  1099.         LastFitID = 74; {Uncalibrated OD}
  1100.         UnitOfMeasureID = 75;
  1101.         OpenID = 77;
  1102.         SaveID = 78;
  1103.         InvertID = 81;
  1104.     var
  1105.         mylog: DialogPtr;
  1106.         ignore, item, i, nBadReals: integer;
  1107.         str: str255;
  1108.         NewValues: StandardsArray;
  1109.     begin
  1110.         with info^ do begin
  1111.             mylog := GetNewDialog(20, nil, pointer(-1));
  1112.             nStandards := mCount;
  1113.             if nStandards > MaxStandards then
  1114.                 nStandards := MaxStandards;
  1115.             for i := 1 to nStandards do begin
  1116.                     SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  1117.                     if (i <= nKnownValues) and (StandardValues[i] <> BadReal) then
  1118.                         SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  1119.                 end;
  1120.             SelectdialogItemText(MyLog, FirstStandardID, 0, 32767);
  1121.             if fit = SpareFit1 then
  1122.                 fit := Uncalibrated;
  1123.             SetDlogItem(mylog, FirstFitID + ord(fit), 1);
  1124.             if fit <> uncalibrated then
  1125.                 SetDString(MyLog, UnitOfMeasureID, UnitOfMeasure);
  1126.             repeat
  1127.                 ModalDialog(nil, item);
  1128.                 if (item >= FirstStandardID) and (item < (FirstStandardID + MaxStandards)) then begin
  1129.                         i := item - FirstStandardID + 1;
  1130.                         if i <= nStandards then
  1131.                             StandardValues[i] := GetDReal(MyLog, item)
  1132.                         else begin
  1133.                                 PutError('Before entering known values you must use the Measure command to read a set of standards.');
  1134.                                 SetDString(MyLog, item, '');
  1135.                             end;
  1136.                         if i > nKnownValues then
  1137.                             nKnownValues := i;
  1138.                     end;
  1139.                 if (item >= FirstLevelID) and (item < (FirstLevelID + MaxStandards)) then begin
  1140.                         i := item - FirstLevelID + 1;
  1141.                         if OptionKeyWasDown and (i <= nStandards) then
  1142.                             umean[item - FirstLevelID + 1] := GetDReal(MyLog, item)
  1143.                         else begin
  1144.                                 PutError('Use the Measure command to record measured values.');
  1145.                                 if i <= nStandards then begin
  1146.                                         RealToString(umean[i], 1, 2, str);
  1147.                                         SetDString(MyLog, item, str)
  1148.                                     end
  1149.                                 else
  1150.                                     SetDString(MyLog, item, '');
  1151.                             end;
  1152.                     end;
  1153.                 if (item >= FirstFitID) and (item <= LastFitID) then begin
  1154.                         for i := FirstFitID to LastFitID do
  1155.                             SetDlogItem(mylog, i, 0);
  1156.                         SetDlogItem(mylog, item, 1);
  1157.                         fit := CurveFitType(item - FirstFitID);
  1158.                     end;
  1159.                 if item = UnitOfMeasureID then begin
  1160.                     str := GetDString(MyLog, item);
  1161.                     TruncateString(str, maxUM);
  1162.                     UnitOfMeasure := str;
  1163.                 end;
  1164.                 if item = OpenID then begin
  1165.                         GetStandardsFromFile(mylog, FirstLevelID, FirstStandardID);
  1166.                         nKnownValues := nStandards;
  1167.                     end;
  1168.                 if (item = SaveID) and (nStandards > 1) then
  1169.                     SaveStandardsToFile(nStandards);
  1170.                 if (item = InvertID) and (nStandards > 1) then
  1171.                     if InvertOD(NewValues) then
  1172.                         for i := 1 to nStandards do begin
  1173.                                 StandardValues[i] := NewValues[i];
  1174.                                 SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 5);
  1175.                             end;
  1176.             until (item = ok) or (item = cancel);
  1177.             DisposeDialog(mylog);
  1178.             DoCalibrateDialog:=item <> cancel;
  1179.         end; {with info^}
  1180.     end; {DoCalibrateDialog}
  1181.  
  1182.  
  1183.     procedure Calibrate;
  1184.         var
  1185.             nBadReals, i: integer;
  1186.             SaveStandards, temp: StandardsArray;
  1187.     begin
  1188.         SaveStandards := StandardValues;
  1189.         if not macro then
  1190.             if not DoCalibrateDialog then begin
  1191.                 StandardValues := SaveStandards;
  1192.                 exit(Calibrate);
  1193.             end;
  1194.         with info^ do begin
  1195.                 if fit = uncalibrated then begin
  1196.                         RemoveDensityCalibration;
  1197.                         exit(calibrate)
  1198.                     end;
  1199.                 nBadReals := 0;
  1200.                 if nStandards > nKnownValues then
  1201.                     nStandards := nKnownValues;
  1202.                 if fit = UncalibratedOD then
  1203.                     SetupUncalibratedOD
  1204.                 else begin
  1205.                         for i := 1 to nStandards do
  1206.                             if StandardValues[i] = BadReal then
  1207.                                 nBadReals := nBadReals + 1;
  1208.                         if (nStandards > 0) and (nBadReals = 0) then
  1209.                             DoCurveFitting
  1210.                         else if fit = uncalibrated then
  1211.                             beep;
  1212.                     end;
  1213.                 if fit <> uncalibrated then begin
  1214.                         if not macro then
  1215.                             SetupCalibrationPlot;
  1216.                     end;
  1217.                 NoInfo^.fit := fit;
  1218.                 NoInfo^.nCoefficients := nCoefficients;
  1219.                 NoInfo^.Coefficient := Coefficient;
  1220.                 NoInfo^.ZeroClip := ZeroClip;
  1221.                 NoInfo^.UnitOfMeasure := UnitOfMeasure;
  1222.                 if (fit<>StraightLine) or (Coefficient[2] <> -1.0) then
  1223.                     InvertPixelValues:=false;
  1224.                 UpdateTitleBar;
  1225.             end; {with info^}
  1226.     end; {Calibrate}
  1227.  
  1228.  
  1229.     procedure ResetCounter;
  1230.         var
  1231.             AlertID: Integer;
  1232.     begin
  1233.         if UnsavedResults and (not macro) then begin
  1234.                 InitCursor;
  1235.                 AlertID := alert(500, nil);
  1236.             end
  1237.         else
  1238.             AlertID := ok;
  1239.         if AlertID <> CancelResetID then begin
  1240.                 nPoints := 0;
  1241.                 nLengths := 0;
  1242.                 nAngles := 0;
  1243.                 mCount := 0;
  1244.                 mCount2 := 0;
  1245.                 UnsavedResults := false;
  1246.                 ShowInfo;
  1247.                 if ResultsWindow <> nil then begin
  1248.                         with ListTE^^ do
  1249.                             TESetSelect(0, teLength, ListTE);
  1250.                         TEDelete(ListTE);
  1251.                         UpdateResultsScrollBars;
  1252.                     end;
  1253.             end;
  1254.         measuring := false;
  1255.     end;
  1256.  
  1257.  
  1258.     procedure ShowResults;
  1259.         const
  1260.             FontSize = 9;
  1261.         var
  1262.             wrect, crect, trect: rect;
  1263.             loc: point;
  1264.     begin
  1265.         mCount2 := mCount;
  1266.         if ResultsWindow <> nil then begin
  1267.                 SelectWindow(ResultsWindow);
  1268.                 exit(ShowResults);
  1269.             end;
  1270.         CopyResultsToBuffer(1, mCount, true);
  1271.         ShowMessage('');
  1272.         ResultsWidth := 110 + round(nListColumns * FieldWidth * 6.5);
  1273.         if ResultsWidth < 250 then
  1274.             ResultsWidth := 250;
  1275.         if (ResultsWidth + 20) > ScreenWidth then
  1276.             ResultsWidth := ScreenWidth - 20;
  1277.         ResultsHeight := ((TextBufLineCount * 2) + 2) * FontSize;
  1278.         if ResultsHeight < 200 then
  1279.             ResultsHeight := 200;
  1280.         if (ResultsHeight + ResultsTop + 50) > ScreenHeight then
  1281.             ResultsHeight := ScreenHeight - ResultsTop - 50;
  1282.         SetRect(wrect, ResultsLeft, ResultsTop, ResultsLeft + ResultsWidth, ResultsTop + ResultsHeight);
  1283.         ResultsWindow := NewWindow(nil, wrect, 'Results', true, 0, pointer(-1), true, 0);
  1284.         WindowPeek(ResultsWindow)^.WindowKind := ResultsKind;
  1285.         SetRect(crect, ResultsWidth - ScrollBarWidth, -1, ResultsWidth + 1, ResultsHeight - 14);
  1286.         vScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsHeight - 14, ScrollBarProc, 0);
  1287.         SetRect(crect, -1, ResultsHeight - ScrollBarWidth, ResultsWidth - 14, ResultsHeight + 1);
  1288.         hScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsWidth - 14, ScrollBarProc, 0);
  1289.         InitResultsTextEdit(Monaco, FontSize);
  1290.         DrawControls(ResultsWindow);
  1291.         WhatToUndo := NothingToUndo;
  1292.     end;
  1293.  
  1294.  
  1295.     procedure DoMeasurementOptions;
  1296.         const
  1297.             FirstID = 3;
  1298.             LastID = 15;
  1299.             RedirectID = 22;
  1300.             IncludeHolesID = 23;
  1301.             AutoID = 24;
  1302.             AdjustID = 25;
  1303.             HeadingsID = 26;
  1304.             MaxMeasurementsID = 21;
  1305.             WidthID = 19;
  1306.             PrecisionID = 17;
  1307.         var
  1308.             mylog: DialogPtr;
  1309.             item, i, SavePrecision, SaveMaxMeasurements, SaveWidth: integer;
  1310.             mtype: MeasurementTypes;
  1311.             SaveMeasurements: SetOfMeasurements;
  1312.             SaveRedirect: boolean;
  1313.             SaveAuto, SaveAdjust, SaveHeadings: boolean;
  1314.     begin
  1315.         InitCursor;
  1316.         if nPoints > 0 then
  1317.             Measurements := Measurements + [XYLocM];
  1318.         if nLengths > 0 then
  1319.             Measurements := Measurements + [LengthM];
  1320.         if nAngles > 0 then
  1321.             Measurements := Measurements + [AngleM];
  1322.         SaveMeasurements := measurements;
  1323.         SaveRedirect := RedirectSampling;
  1324.         SaveWidth := FieldWidth;
  1325.         SavePrecision := precision;
  1326.         SaveAuto := WandAutoMeasure;
  1327.         SaveAdjust := WandAdjustAreas;
  1328.         SaveMaxMeasurements := MaxMeasurements;
  1329.         SaveHeadings := ShowHeadings;
  1330.         mylog := GetNewDialog(4000, nil, pointer(-1));
  1331.         mtype := AreaM;
  1332.         for i := FirstID to LastID do begin
  1333.                 if mtype in measurements then
  1334.                     SetDlogItem(mylog, i, 1);
  1335.                 if i <> LastID then
  1336.                     mtype := succ(mtype);
  1337.             end;
  1338.         SetDlogItem(mylog, RedirectID, ord(RedirectSampling));
  1339.         SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1340.         SetDlogItem(mylog, AutoID, ord(WandAutoMeasure));
  1341.         SetDlogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1342.         SetDlogItem(mylog, HeadingsID, ord(ShowHeadings));
  1343.         SetDNum(MyLog, MaxMeasurementsID, MaxMeasurements);
  1344.         SetDNum(MyLog, WidthID, FieldWidth);
  1345.         SetDNum(MyLog, PrecisionID, precision);
  1346.         repeat
  1347.             ModalDialog(nil, item);
  1348.             if (item >= FirstID) and (item <= LastID) then begin
  1349.                     i := item - FirstID;
  1350.                     case i of
  1351.                         0: 
  1352.                             mtype := AreaM;
  1353.                         1: 
  1354.                             mtype := MeanM;
  1355.                         2: 
  1356.                             mtype := StdDevM;
  1357.                         3: 
  1358.                             mtype := xyLocM;
  1359.                         4: 
  1360.                             mtype := ModeM;
  1361.                         5: 
  1362.                             mtype := LengthM;
  1363.                         6: 
  1364.                             mtype := MajorAxisM;
  1365.                         7: 
  1366.                             mtype := MinorAxisM;
  1367.                         8: 
  1368.                             mtype := AngleM;
  1369.                         9: 
  1370.                             mtype := IntDenM;
  1371.                         10: 
  1372.                             mtype := MinMaxM;
  1373.                         11: 
  1374.                             mtype := User1M;
  1375.                         12: 
  1376.                             mtype := User2M;
  1377.                     end;
  1378.                     if mtype in measurements then begin
  1379.                             measurements := measurements - [mtype];
  1380.                             SetDlogItem(mylog, item, 0)
  1381.                         end
  1382.                     else begin
  1383.                             measurements := measurements + [mtype];
  1384.                             SetDlogItem(mylog, item, 1)
  1385.                         end;
  1386.                 end;
  1387.             if item = RedirectID then begin
  1388.                     RedirectSampling := not RedirectSampling;
  1389.                     SetDlogItem(mylog, RedirectID, ord(RedirectSampling));
  1390.                 end;
  1391.             if item = IncludeHolesID then begin
  1392.                     IncludeHoles := not IncludeHoles;
  1393.                     SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1394.                 end;
  1395.             if item = AutoID then begin
  1396.                     WandAutoMeasure := not WandAutoMeasure;
  1397.                     SetDlogItem(mylog, AutoID, ord(WandAutoMeasure));
  1398.                 end;
  1399.             if item = AdjustID then begin
  1400.                     WandAdjustAreas := not WandAdjustAreas;
  1401.                     SetDlogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1402.                 end;
  1403.             if item = HeadingsID then begin
  1404.                     ShowHeadings := not ShowHeadings;
  1405.                     SetDlogItem(mylog, HeadingsID, ord(ShowHeadings));
  1406.                 end;
  1407.             if item = WidthID then
  1408.                 FieldWidth := GetDNum(MyLog, WidthID);
  1409.             if item = PrecisionID then
  1410.                 precision := GetDNum(MyLog, PrecisionID);
  1411.             if item = MaxMeasurementsID then
  1412.                 MaxMeasurements := GetDNum(MyLog, MaxMeasurementsID);
  1413.         until (item = ok) or (item = cancel);
  1414.         DisposeDialog(mylog);
  1415.         if (FieldWidth < 1) or (FieldWidth > 18) then begin
  1416.                 FieldWidth := SaveWidth;
  1417.                 beep;
  1418.             end;
  1419.         if (precision < 0) or (precision > 8) then begin
  1420.                 precision := SavePrecision;
  1421.                 beep;
  1422.             end;
  1423.         if (MaxMeasurements < 1) or (MaxMeasurements > MaxMaxRegions) then begin
  1424.                 MaxMeasurements := SaveMaxMeasurements;
  1425.                 beep;
  1426.             end;
  1427.         if item = cancel then begin
  1428.                 measurements := SaveMeasurements;
  1429.                 RedirectSampling := SaveRedirect;
  1430.                 FieldWidth := SaveWidth;
  1431.                 precision := SavePrecision;
  1432.                 WandAutoMeasure := SaveAuto;
  1433.                 WandAdjustAreas := SaveAdjust;
  1434.                 MaxMeasurements := SaveMaxMeasurements;
  1435.                 ShowHeadings := SaveHeadings;
  1436.             end;
  1437.         if not (XYLocM in Measurements) then
  1438.             nPoints := 0;
  1439.         if not (LengthM in Measurements) then
  1440.             nLengths := 0;
  1441.         if not (AngleM in Measurements) then
  1442.             nAngles := 0;
  1443.         UpdateFitEllipse;
  1444.         if MaxMeasurements <> SaveMaxMeasurements then begin
  1445.                 PutError('You must quit and restart NIH Image before the change to Max Measurements will take effect.');
  1446.                 SaveSettings;
  1447.                 MaxMeasurements:=SaveMaxMeasurements;
  1448.             end;
  1449.         if (Measurements <> SaveMeasurements) or (SaveWidth <> FieldWidth) or (SavePrecision <> Precision) then
  1450.             UpdateList;
  1451.     end;
  1452.  
  1453.  
  1454.     procedure UpdateRoiLineWidth;
  1455.     begin
  1456.         with info^, info^.RoiRect do
  1457.             if RoiShowing and (RoiType = LineRoi) then begin
  1458.                     LX1 := left + LX1;
  1459.                     LY1 := top + LY1;
  1460.                     LX2 := left + LX2;
  1461.                     LY2 := top + LY2;
  1462.                     MakeRegion;
  1463.                 end;
  1464.     end;
  1465.  
  1466.  
  1467.     procedure DoProfilePlotOptions;
  1468.         const
  1469.             FixedScaleID = 7;
  1470.             MinID = 8;
  1471.             MaxID = 9;
  1472.             FixedSizeID = 10;
  1473.             WidthID = 11;
  1474.             HeightID = 12;
  1475.             LineWidthID = 13;
  1476.             LinePlotID = 14;
  1477.             ScatterPlotID = 15;
  1478.             InvertID = 16;
  1479.             LabelsID = 17;
  1480.         var
  1481.             mylog: DialogPtr;
  1482.             item, i: integer;
  1483.             SaveAutoscale, SaveLinePlot, SaveInvert, SaveDrawLabels, SaveFixedSize: boolean;
  1484.             SaveWidth, SaveHeight, SaveLineWidth, SaveLineIndex: integer;
  1485.             SaveMin, SaveMax: extended;
  1486.     begin
  1487.         InitCursor;
  1488.         SaveAutoscale := AutoscalePlots;
  1489.         SaveLinePlot := LinePlot;
  1490.         SaveInvert := InvertPlots;
  1491.         SaveMin := ProfilePlotMin;
  1492.         SaveMax := ProfilePlotMax;
  1493.         SaveLineWidth := LineWidth;
  1494.         SaveLineIndex := LineIndex;
  1495.         SaveWidth := ProfilePlotWidth;
  1496.         SaveHeight := ProfilePlotHeight;
  1497.         SaveDrawLabels := DrawPlotLabels;
  1498.         mylog := GetNewDialog(5000, nil, pointer(-1));
  1499.         SetDlogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1500.         SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1501.         SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1502.         SetDlogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1503.         SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1504.         SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1505.         if LinePlot then
  1506.             SetDlogItem(mylog, LinePlotID, 1)
  1507.         else
  1508.             SetDlogItem(mylog, ScatterPlotID, 1);
  1509.         if InvertPlots then
  1510.             SetDlogItem(mylog, InvertID, 1);
  1511.         if DrawPlotLabels then
  1512.             SetDlogItem(mylog, LabelsID, 1);
  1513.         SetDNum(MyLog, LineWidthID, LineWidth);
  1514.         repeat
  1515.             ModalDialog(nil, item);
  1516.             if item = FixedScaleID then begin
  1517.                     AutoscalePlots := not AutoscalePlots;
  1518.                     SetDlogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1519.                 end;
  1520.             if item = MinID then begin
  1521.                     ProfilePlotMin := GetDReal(MyLog, MinID);
  1522.                     AutoscalePlots := false;
  1523.                     SetDlogItem(mylog, FixedScaleID, 1);
  1524.                 end;
  1525.             if item = MaxID then begin
  1526.                     ProfilePlotMax := GetDReal(MyLog, MaxID);
  1527.                     AutoscalePlots := false;
  1528.                     SetDlogItem(mylog, FixedScaleID, 1);
  1529.                 end;
  1530.             if item = FixedSizeID then begin
  1531.                     FixedSizePlot := not FixedSizePlot;
  1532.                     SetDlogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1533.                 end;
  1534.             if item = WidthID then begin
  1535.                     ProfilePlotWidth := GetDNum(MyLog, WidthID);
  1536.                     if (ProfilePlotWidth < 0) or (ProfilePlotWidth > 1023) then begin
  1537.                             ProfilePlotWidth := SaveWidth;
  1538.                             SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1539.                         end;
  1540.                     FixedSizePlot := true;
  1541.                     SetDlogItem(mylog, FixedSizeID, 1);
  1542.                 end;
  1543.             if item = HeightID then begin
  1544.                     ProfilePlotHeight := GetDNum(MyLog, HeightID);
  1545.                     if (ProfilePlotHeight < 0) or (ProfilePlotHeight > 1023) then begin
  1546.                             ProfilePlotHeight := SaveHeight;
  1547.                             SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1548.                         end;
  1549.                     FixedSizePlot := true;
  1550.                     SetDlogItem(mylog, FixedSizeID, 1);
  1551.                 end;
  1552.             if (item = LinePlotID) or (item = ScatterPlotID) then begin
  1553.                     SetDlogItem(mylog, LinePlotID, 0);
  1554.                     SetDlogItem(mylog, ScatterPlotID, 0);
  1555.                     SetDlogItem(mylog, item, 1);
  1556.                     LinePlot := item = LinePlotID;
  1557.                 end;
  1558.             if item = InvertID then begin
  1559.                     InvertPlots := not InvertPlots;
  1560.                     SetDlogItem(mylog, InvertID, ord(InvertPlots));
  1561.                 end;
  1562.             if item = LabelsID then begin
  1563.                     DrawPlotLabels := not DrawPlotLabels;
  1564.                     if DrawPlotLabels then {Attempt to fix a "sticky" check box bug.}
  1565.                         SetDlogItem(mylog, LabelsID, 1)
  1566.                     else
  1567.                         SetDlogItem(mylog, LabelsID, 0);
  1568.                 end;
  1569.             if item = LineWidthID then begin
  1570.                     LineWidth := GetDNum(MyLog, LineWidthID);
  1571.                     if (LineWidth < 1) or (LineWidth > 500) then begin
  1572.                             LineWidth := SaveLineWidth;
  1573.                             SetDNum(MyLog, LineWidthID, LineWidth);
  1574.                         end;
  1575.                     ShowLineWidth;
  1576.                 end;
  1577.         until (item = ok) or (item = cancel);
  1578.         DisposeDialog(mylog);
  1579.         if item = cancel then begin
  1580.                 ProfilePlotWidth := SaveWidth;
  1581.                 ProfilePlotHeight := SaveHeight;
  1582.                 AutoscalePlots := SaveAutoscale;
  1583.                 LinePlot := SaveLinePlot;
  1584.                 InvertPlots := SaveInvert;
  1585.                 ProfilePlotMin := SaveMin;
  1586.                 ProfilePlotMax := SaveMax;
  1587.                 DrawPlotLabels := SaveDrawLabels;
  1588.                 LineWidth := SaveLineWidth;
  1589.                 if LineIndex <> SaveLineIndex then begin
  1590.                         LineIndex := SaveLineIndex;
  1591.                         DrawTools;
  1592.                     end;
  1593.             end;
  1594.         if LineWidth <> SaveLineWidth then
  1595.             UpdateRoiLineWidth;
  1596.         if ProfilePlotMax <= ProfilePlotMin then begin
  1597.                 ProfilePlotMin := SaveMin;
  1598.                 ProfilePlotMax := SaveMax;
  1599.             end;
  1600.     end;
  1601.  
  1602.  
  1603.     procedure DoPoints (event: EventRecord);
  1604.         var
  1605.             loc, tloc: point;
  1606.             hloc, vloc, y, offset: LongInt;
  1607.             r: rect;
  1608.             str, str1, str2: str255;
  1609.             Decrement: boolean;
  1610.             SaveGDevice: GDHandle;
  1611.     begin
  1612.         Decrement := false;
  1613.         SaveGDevice := GetGDevice;
  1614.         SetGDevice(osGDevice);
  1615.         SetPort(GrafPtr(info^.osPort));
  1616.         pmForeColor(ForegroundIndex);
  1617.         loc := event.where;
  1618.         ScreenToOffscreen(loc);
  1619.         with loc do begin
  1620.                 hloc := h;
  1621.                 vloc := v;
  1622.             end;
  1623.         with results, Info^ do begin
  1624.                 nPoints := nPoints + 1;
  1625.                 IncrementCounter;
  1626.                 if InvertYCoordinates then
  1627.                     y := info^.PicRect.bottom - vloc - 1
  1628.                 else
  1629.                     y := vloc;
  1630.                 ClearResults(mCount);
  1631.                 PixelCount^[mCount] := 1;
  1632.                 if SpatiallyCalibrated then
  1633.                     mArea^[mCount] := 1.0 / xScale * yScale
  1634.                 else
  1635.                     mArea^[mCount] := 1;
  1636.                 mean^[mCount] := cvalue[MyGetPixel(hloc, vloc)];
  1637.                 with info^ do
  1638.                     if SpatiallyCalibrated then begin
  1639.                             xcenter^[mCount] := hloc / xScale;
  1640.                             ycenter^[mCount] := y / yScale;
  1641.                         end
  1642.                     else begin
  1643.                             xcenter^[mCount] := hloc;
  1644.                             ycenter^[mCount] := y;
  1645.                         end;
  1646.             end;
  1647.         PenNormal;
  1648.         if OptionKeyDown then begin
  1649.                 NumToString(mCount, str);
  1650.                 tloc := loc;
  1651.                 tloc.v := tloc.v + CurrentSize div 3;
  1652.                 DrawTextString(str, tloc, TeJustCenter);
  1653.             end
  1654.         else begin
  1655.                 offset := LineWidth div 2;
  1656.                 SetRect(r, hloc - offset, vloc - offset, hloc + offset + 1, vloc + offset + 1);
  1657.                 if ShiftKeyDown then begin
  1658.                         Decrement := true;
  1659.                         EraseOval(r);
  1660.                         mcount := mcount - 2;
  1661.                         if mcount <= 0 then begin
  1662.                                 mcount := 0;
  1663.                                 UnsavedResults := false;
  1664.                             end;
  1665.                         nPoints := nPoints - 2;
  1666.                         if nPoints < 0 then
  1667.                             nPoints := 0;
  1668.                     end
  1669.                 else
  1670.                     PaintOval(r);
  1671.                 UpdateScreen(r);
  1672.                 if ControlKeyDown then
  1673.                     with info^ do begin
  1674.                             if SpatiallyCalibrated then begin
  1675.                                     RealToString(hloc / xScale, 1, Precision, str1);
  1676.                                     RealToString(y / yScale, 1, Precision, str2);
  1677.                                 end
  1678.                             else begin
  1679.                                     NumToString(hloc, str1);
  1680.                                     NumToString(y, str2);
  1681.                                 end;
  1682.                             tloc := loc;
  1683.                             with tloc do begin
  1684.                                     h := h + offset + 5;
  1685.                                     v := v + CurrentSize div 3;
  1686.                                 end;
  1687.                             str := concat('(', str1, ', ', str2, ')');
  1688.                             DrawTextString(str, tloc, TeJustLeft);
  1689.                         end; {Control Key Down}
  1690.             end;
  1691.         SetGDevice(SaveGDevice);
  1692.         InfoMessage := '';
  1693.         ShowInfo;
  1694.         if Decrement then begin
  1695.                 DeleteLines(mcount + 1, mcount + 1);
  1696.                 WhatToUndo := NothingToUndo;
  1697.             end
  1698.         else begin
  1699.                 AppendResults;
  1700.                 if (nPoints = 1) then
  1701.                     if not (XYlocM in Measurements) then
  1702.                         UpdateList;
  1703.                 measuring := true;
  1704.                 WhatToUndo := UndoPoint;
  1705.             end;
  1706.     end;
  1707.  
  1708.  
  1709.     procedure FindAngle (event: EventRecord);
  1710.         var
  1711.             start, finish, OldFinish, MidPoint, first: point;
  1712.             ticks: LongInt;
  1713.             x1, y1, x2, y2: integer;
  1714.             angle, angle1, angle2: extended;
  1715.             StartRect: rect;
  1716.             FirstLineDone: boolean;
  1717.     begin
  1718.         if NoUndo then
  1719.             exit(FindAngle);
  1720.         DrawLabels('Angle:', '', '');
  1721.         FlushEvents(EveryEvent, 0);
  1722.         start := event.where;
  1723.         Pt2Rect(start, start, StartRect);
  1724.         InsetRect(StartRect, -2, -2);
  1725.         finish := start;
  1726.         SetPort(info^.wptr);
  1727.         PenNormal;
  1728.         PenMode(PatXor);
  1729.         PenSize(1, 1);
  1730.         MoveTo(start.h, start.v);
  1731.         first := start;
  1732.         repeat
  1733.             repeat
  1734.                 OldFinish := finish;
  1735.                 GetMouse(finish);
  1736.                 MoveTo(start.h, start.v);
  1737.                 LineTo(OldFinish.h, OldFinish.v);
  1738.                 MoveTo(start.h, start.v);
  1739.                 LineTo(finish.h, finish.v);
  1740.                 ticks := TickCount;
  1741.                 while ticks = TickCount do
  1742.                     ;
  1743.                 x1 := finish.h - start.h;
  1744.                 y1 := start.v - finish.v;
  1745.                 angle1 := GetAngle(x1, info^.PixelAspectRatio * y1);
  1746.                 Show1Value(angle1, NoValue);
  1747.             until GetNextEvent(mUpMask, event);
  1748.             FirstLineDone := not PtInRect(finish, StartRect);
  1749.             if not FirstLineDone then
  1750.                 start := finish;
  1751.         until FirstLineDone;
  1752.         MidPoint := finish;
  1753.         x1 := start.h - MidPoint.h;
  1754.         y1 := MidPoint.v - start.v;
  1755.         angle1 := GetAngle(x1, info^.PixelAspectRatio * y1);
  1756.         start := finish;
  1757.         finish := start;
  1758.         repeat
  1759.             OldFinish := finish;
  1760.             GetMouse(finish);
  1761.             MoveTo(start.h, start.v);
  1762.             LineTo(OldFinish.h, OldFinish.v);
  1763.             MoveTo(start.h, start.v);
  1764.             LineTo(finish.h, finish.v);
  1765.             ticks := TickCount;
  1766.             while ticks = TickCount do
  1767.                 ;
  1768.             x2 := finish.h - MidPoint.h;
  1769.             y2 := MidPoint.v - finish.v;
  1770.             angle2 := GetAngle(x2, info^.PixelAspectRatio * y2);
  1771.             with results do begin
  1772.                     if angle1 >= angle2 then
  1773.                         angle := angle1 - angle2
  1774.                     else
  1775.                         angle := angle2 - angle1;
  1776.                     if angle > 180.0 then
  1777.                         angle := 360.0 - angle;
  1778.                     Show1Value(angle, NoValue);
  1779.                 end;
  1780.         until GetNextEvent(mUpMask, event);
  1781.         nAngles := nAngles + 1;
  1782.         IncrementCounter;
  1783.         ClearResults(mCount);
  1784.         Orientation^[mCount] := angle;
  1785.         InfoMessage := '';
  1786.         ShowInfo;
  1787.         AppendResults;
  1788.         if nAngles = 1 then
  1789.             UpdateList;
  1790.         repeat
  1791.         until not GetNextEvent(EveryEvent, Event); {FlushEvent doesn't work under A/UX!}
  1792.         xCoordinates^[1] := first.h;
  1793.         yCoordinates^[1] := first.v;
  1794.         xCoordinates^[2] := midpoint.h;
  1795.         yCoordinates^[2] := midpoint.v;
  1796.         xCoordinates^[3] := finish.h;
  1797.         yCoordinates^[3] := finish.v;
  1798.         nCoordinates := 3;
  1799.         MakeNonStraightLineRoi(SegLineRoi);
  1800.     end;
  1801.  
  1802.  
  1803.     procedure SaveBlankField;
  1804.         var
  1805.             SaveInfo: InfoPtr;
  1806.             i, xLines, xPixelsPerLine: integer;
  1807.             src, dst: ptr;
  1808.             SaveFlag: boolean;
  1809.             name: str255;
  1810.     begin
  1811.         if info^.PictureType = FrameGrabberType then begin
  1812.                 GetWTitle(info^.wptr, name);
  1813.                 if pos('(Corrected)', name) > 0 then begin
  1814.                         PutError('To save a blank field the captured image must be uncorrected.');
  1815.                         exit(SaveBlankField);
  1816.                     end;
  1817.                 SaveInfo := info;
  1818.                 if BlankFieldInfo = nil then begin
  1819.                         if not Duplicate('Blank Field', true) then
  1820.                             exit(SaveBlankField);
  1821.                     end;
  1822.                 src := info^.PicBaseAddr;
  1823.                 dst := BlankFieldInfo^.PicBaseAddr;
  1824.                 with Info^.PicRect do begin
  1825.                         xLines := bottom - top;
  1826.                         xPixelsPerLine := right - left;
  1827.                     end;
  1828.                 for i := 1 to xLines do begin
  1829.                         BlockMove(src, dst, xPixelsPerLine);
  1830.                         src := ptr(ord4(src) + info^.BytesPerRow);
  1831.                         dst := ptr(ord4(dst) + xPixelsPerLine);
  1832.                     end;
  1833.                 Info := BlankFieldInfo;
  1834.                 InvertPic;
  1835.                 SaveFlag := digitizing;
  1836.                 digitizing := false;
  1837.                 SelectAll(false);
  1838.                 ShowCount := false;
  1839.                 Measure;
  1840.                 ShowCount := true;
  1841.                 digitizing := SaveFlag;
  1842.                 BlankFieldMean := round(results.UncalibratedMean);
  1843.                 UndoLastMeasurement(false);
  1844.                 KillRoi;
  1845.                 UpdatePicWindow;
  1846.                 info := SaveInfo;
  1847.                 SelectWindow(Info^.wptr);
  1848.             end;
  1849.     end;
  1850.  
  1851.  
  1852.     procedure UndoLastMeasurement (DisplayResults: boolean);
  1853.     begin
  1854.         if mCount > 0 then begin
  1855.                 if DisplayResults then
  1856.                     DeleteLines(mCount, mCount);
  1857.                 mCount := mCount - 1;
  1858.                 if mCount = 0 then
  1859.                     UnsavedResults := false;
  1860.             end
  1861.         else
  1862.             WhatToUndo := NothingToUndo;
  1863.         if DisplayResults then
  1864.             ShowInfo;
  1865.     end;
  1866.  
  1867.  
  1868.     function PixelInside (hloc, vloc: integer): boolean;
  1869.         var
  1870.             value: integer;
  1871.     begin
  1872.         value := MyGetPixel(hloc, vloc);
  1873.         case ThresholdingMode of
  1874.             DensitySlice: 
  1875.                 PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  1876.             GrayMapThresholding: 
  1877.                 PixelInside := value >= GrayMapThreshold;
  1878.             BinaryImage: 
  1879.                 PixelInside := value = BlackIndex;
  1880.         end;
  1881.     end;
  1882.  
  1883.  
  1884.     function TraceEdge (hstart, vstart: integer; StartingDirection: char; var TouchingEdge: boolean): boolean;
  1885.  
  1886.    {Traces the points(not pixels) that define the edge of an object using the following}
  1887.    {16 entry lookup table and converts the resulting outline to a QuickDraw region.}
  1888.  
  1889.       {Index  1234*  Code    Result}
  1890.  
  1891.       {0         0000     X      Should never happen}
  1892.       {1         000X     R      Go Right}
  1893.       {2         00X0     D     Go Down}
  1894.       {3         00XX     R     Go Right}
  1895.       {4         0X00     U     Go Up}
  1896.       {5         0X0X     U     Go Up}
  1897.       {6         0XX0     u      Go up or down depending on current direction}
  1898.       {7         0XXX     U      Go up}
  1899.       {8         X000     L      Go left}
  1900.       {9         X00X     l       Go left or right depending on current direction}
  1901.       {10        X0X0     D      Go down}
  1902.       {11        X0XX     R      Go right}
  1903.       {12        XX00     L      Go left}
  1904.       {13        XX0X     L      Go left}
  1905.       {14        XXX0     D     Go down}
  1906.       {15        XXXX     X     Should never happen}
  1907.  
  1908.        {*   1=Upper left pixel,  2=Upper right pixel, 3=Lower left pixel, 4=Lower right pixel}
  1909.  
  1910.         var
  1911.             count, hloc, vloc, index, SaveBackground: integer;
  1912.             Saveport: GrafPtr;
  1913.             direction, NewDirection: char;
  1914.             table: string[16];
  1915.             UL, UR, LL, LR, SaveCoordinates: boolean;
  1916.             TempRgn: RgnHandle;
  1917.     begin
  1918.         TouchingEdge := false;
  1919.         table := 'XRDRUUuULlDRLLDX';
  1920.         GetPort(SavePort);
  1921.         SetPort(GrafPtr(info^.osPort));
  1922.         if SelectionMode <> NewSelection then
  1923.             TempRgn := NewRgn;
  1924.         with info^ do begin
  1925.                 SaveBackground := BackgroundIndex; {We want MyGetPixel to always return 0}
  1926.                 BackgroundIndex := WhiteIndex;     {for coordinates beyond the edge of the image.}
  1927.                 PenNormal;
  1928.                 OpenRgn;
  1929.                 direction := StartingDirection;
  1930.                 hloc := hstart;
  1931.                 vloc := vstart;
  1932.                 UL := PixelInside(hloc - 1, vloc - 1);
  1933.                 UR := PixelInside(hloc, vloc - 1);
  1934.                 LL := PixelInside(hloc - 1, vloc);
  1935.                 LR := PixelInside(hloc, vloc);
  1936.                 MoveTo(hstart, vstart);
  1937.                 SaveCoordinates := not MakingLOI;
  1938.                 if SaveCoordinates then begin
  1939.                         xCoordinates^[1] := hstart;
  1940.                         yCoordinates^[1] := vstart;
  1941.                     end;
  1942.                 count := 1;
  1943.                 repeat
  1944.                     if IgnoreParticlesTouchingEdge then
  1945.                         with info^.PicRect do
  1946.                             TouchingEdge := TouchingEdge or (hloc = left) or (hloc = right) or (vloc = top) or (vloc = bottom);
  1947.                     index := 0;
  1948.                     if LR then
  1949.                         index := bor(index, 1);
  1950.                     if LL then
  1951.                         index := bor(index, 2);
  1952.                     if UR then
  1953.                         index := bor(index, 4);
  1954.                     if UL then
  1955.                         index := bor(index, 8);
  1956.                     NewDirection := table[index + 1];
  1957.                     if NewDirection = 'u' then begin
  1958.                             if direction = 'R' then
  1959.                                 NewDirection := 'U'
  1960.                             else
  1961.                                 NewDirection := 'D'
  1962.                         end;
  1963.                     if NewDirection = 'l' then begin
  1964.                             if direction = 'U' then
  1965.                                 NewDirection := 'L'
  1966.                             else
  1967.                                 NewDirection := 'R'
  1968.                         end;
  1969.                     if NewDirection <> direction then begin
  1970.                         LineTo(hloc, vloc);
  1971.                         if SaveCoordinates then begin
  1972.                                 xCoordinates^[count] := hloc;
  1973.                                 yCoordinates^[count] := vloc;
  1974.                                 count := count + 1;
  1975.                             end;
  1976.                     end;
  1977.                     case NewDirection of
  1978.                         'U':  begin
  1979.                                 vloc := vloc - 1;
  1980.                                 LL := UL;
  1981.                                 LR := UR;
  1982.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1983.                                 UR := PixelInside(hloc, vloc - 1);
  1984.                             end;
  1985.                         'D':  begin
  1986.                                 vloc := vloc + 1;
  1987.                                 UL := LL;
  1988.                                 UR := LR;
  1989.                                 LL := PixelInside(hloc - 1, vloc);
  1990.                                 LR := PixelInside(hloc, vloc);
  1991.                             end;
  1992.                         'L':  begin
  1993.                                 hloc := hloc - 1;
  1994.                                 UR := UL;
  1995.                                 LR := LL;
  1996.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1997.                                 LL := PixelInside(hloc - 1, vloc);
  1998.                             end;
  1999.                         'R':  begin
  2000.                                 hloc := hloc + 1;
  2001.                                 UL := UR;
  2002.                                 LL := LR;
  2003.                                 UR := PixelInside(hloc, vloc - 1);
  2004.                                 LR := PixelInside(hloc, vloc);
  2005.                             end;
  2006.                     end;
  2007.                     direction := NewDirection;
  2008.                 until ((hloc = hstart) and (vloc = vstart) and (direction = StartingDirection)) or (count >= MaxCoordinates);
  2009.                 LineTo(hstart, vstart);
  2010.                 if SelectionMode <> NewSelection then
  2011.                     CloseRgn(TempRgn)
  2012.                 else
  2013.                     CloseRgn(roiRgn);
  2014.                 {ShowMessage(StringOf(count, '  ', GetHandleSize(handle(roiRgn)))); beep;}
  2015.                 with roiRgn^^.rgnBBox do
  2016.                     if (count >= MaxCoordinates) or (((right - left) = 0) and ((bottom - top) = 0))  then begin
  2017.                         SetEmptyRgn(roiRgn);
  2018.                         SetPort(SavePort);
  2019.                         TraceEdge := false;
  2020.                         BackgroundIndex := SaveBackground;
  2021.                         nCoordinates := 0;
  2022.                         AbortMacro;
  2023.                         PutError(StringOf('Perimeter too long.', cr, '(', count:1, ' coordinates)'));
  2024.                         exit(TraceEdge);
  2025.                     end;
  2026.                 if (SelectionMode = AddSelection) then begin
  2027.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2028.                             UnionRgn(roiRgn, TempRgn, roiRgn);
  2029.                     end
  2030.                 else if (SelectionMode = SubSelection) then begin
  2031.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2032.                             DiffRgn(roiRgn, TempRgn, roiRgn);
  2033.                     end;
  2034.                 RoiShowing := true;
  2035.                 roiType := TracedRoi;
  2036.                 if SelectionMode = SubSelection then
  2037.                     UpdateScreen(RoiRect);
  2038.                 RoiRect := roiRgn^^.rgnBBox;
  2039.                 BackgroundIndex := SaveBackground;
  2040.             end; {with info}
  2041.         if SelectionMode <> NewSelection then
  2042.             DisposeRgn(TempRgn);
  2043.         SetPort(SavePort);
  2044.         if SaveCoordinates then begin
  2045.                 nCoordinates := count - 1;
  2046.                 MakeCoordinatesRelative;
  2047.             end;
  2048.         TraceEdge := true;
  2049.     end;
  2050.  
  2051.  
  2052.     procedure MarkSelection (count: integer);
  2053.         var
  2054.             SavePort: GrafPtr;
  2055.             NumWidth, NumLeft, NumBottom, SaveForegroundIndex: integer;
  2056.             RoiWidth, inset, hcenter, vcenter: integer;
  2057.             str: str255;
  2058.             r: rect;
  2059.             OutlineWithEllipse: boolean;
  2060.             xc, yc: extended;
  2061.             SaveGDevice: GDHandle;
  2062.     begin
  2063.         OutlineWithEllipse := FitEllipse and OptionKeyWasDown;
  2064.         with info^ do begin
  2065.                 KillRoi;
  2066.                 SetupUndo;
  2067.                 WhatToUndo := UndoOutline;
  2068.                 SaveGDevice := GetGDevice;
  2069.                 SetGDevice(osGDevice);
  2070.                 GetPort(SavePort);
  2071.                 SetPort(GrafPtr(osPort));
  2072.                 SaveForegroundIndex := ForegroundIndex;
  2073.                 SetForegroundColor(WhiteIndex);
  2074.                 PenNormal;
  2075.                 TextFont(Geneva);
  2076.                 TextSize(9);
  2077.                 NumToString(count, str);
  2078.                 with RoiRect do begin
  2079.                         NumWidth := StringWidth(str);
  2080.                         if AnalyzingParticles or OutlineWithEllipse then begin
  2081.                                 xc := xcenter^[count];
  2082.                                 yc := ycenter^[count];
  2083.                                 if SpatiallyCalibrated then begin
  2084.                                         xc := xc * xScale;
  2085.                                         yc := yc * yScale;
  2086.                                     end;
  2087.                                 hcenter := round(xc);
  2088.                                 vcenter := round(yc);
  2089.                                 if InvertYCoordinates then
  2090.                                     vcenter := PicRect.bottom - vcenter - 1
  2091.                             end
  2092.                         else begin
  2093.                                 hcenter := left + (right - left) div 2;
  2094.                                 vcenter := top + (bottom - top) div 2;
  2095.                             end;
  2096.                         NumLeft := hcenter - NumWidth div 2;
  2097.                         NumBottom := vcenter + 3;
  2098.                         if not BinaryPic and not AnalyzingParticles then begin
  2099.                                 FrameRgn(roiRgn);
  2100.                                 if OutlineWithEllipse then
  2101.                                     DrawEllipse;
  2102.                             end;
  2103.                     end;
  2104.                 PenNormal;
  2105.                 SetRect(r, NumLeft - 1, NumBottom - 9, NumLeft + NumWidth + 1, NumBottom + 1);
  2106.                 PaintRoundRect(r, 4, 4);
  2107.                 MoveTo(NumLeft, NumBottom);
  2108.                 TextMode(srcXor);
  2109.                 DrawString(str);
  2110.                 SetForegroundColor(SaveForegroundIndex);
  2111.                 if not analyzingParticles then
  2112.                     UpdateScreen(RoiRect);
  2113.                 SetPort(SavePort);
  2114.                 SetGDevice(SaveGDevice);
  2115.                 changes := true;
  2116.             end;
  2117.     end;
  2118.  
  2119.  
  2120.     function isBinaryImage: boolean;
  2121.         var
  2122.             SaveRoiRect: rect;
  2123.             SaveRedirectFlag: boolean;
  2124.     begin
  2125.         with info^ do begin
  2126.                 SaveRoiRect := RoiRect;
  2127.                 RoiRect := PicRect;
  2128.                 if RedirectSampling then
  2129.                     GetHistogram
  2130.                 else
  2131.                     GetRectHistogram;
  2132.                 BinaryPic := (histogram[0] + histogram[255]) = PixelsPerLine * nLines;
  2133.                 isBinaryImage := BinaryPic;
  2134.                 RoiRect := SaveRoiRect;
  2135.             end;
  2136.     end;
  2137.  
  2138.  
  2139.     function SetupAutoOutline (BinaryPixel: boolean): boolean;
  2140.     begin
  2141.         SetupAutoOutline := false;
  2142.         FindThresholdingMode;
  2143.         if (ThresholdingMode = NoThresholding) or MakingLOI then
  2144.             if isBinaryImage or BinaryPixel then
  2145.                 ThresholdingMode := BinaryImage;
  2146.         if ThresholdingMode = NoThresholding then begin
  2147.                 if not macro or AnalyzingParticles then
  2148.                     PutError('Sorry, but you must be thresholding, or working with a binary image, to use the wand tool or to do particle analysis.');
  2149.                 exit(SetupAutoOutline);
  2150.             end;
  2151.         if (ThresholdingMode = GrayMapThresholding) and (GrayMapThreshold = 0) then begin
  2152.                 PutError(' Threshold must be non-zero.');
  2153.                 exit(SetupAutoOutline);
  2154.             end;
  2155.         if not MakingLOI then
  2156.             ShowWatch;
  2157.         SetupAutoOutline := true;
  2158.     end;
  2159.  
  2160.  
  2161.     procedure AutoOutline (start: point);
  2162.         var
  2163.             hloc, vloc: integer;
  2164.             TouchingEdge, BinaryPixel: boolean;
  2165.             direction: char;
  2166.             count: LongInt;
  2167.             Perimeter, CalibratedPerimeter, AspectRatio: extended;
  2168.     begin
  2169.         with start do
  2170.             BinaryPixel := (MyGetPixel(h, v) = WhiteIndex) or (MyGetPixel(h, v) = BlackIndex);
  2171.         if not SetupAutoOutline(BinaryPixel) then
  2172.             exit(AutoOutline);
  2173.         if SelectionMode = NewSelection then
  2174.             KillRoi;
  2175.         with info^ do begin
  2176.                 with start do
  2177.                     if PixelInside(h, v) then begin
  2178.                             repeat
  2179.                                 h := h + 1;
  2180.                             until not PixelInside(h, v) or (h >= PicRect.right);
  2181.                             if not PixelInside(h - 1, v - 1) then
  2182.                                 direction := 'R'
  2183.                             else if PixelInside(h, v - 1) then
  2184.                                 direction := 'L'
  2185.                             else
  2186.                                 direction := 'D';
  2187.                         end
  2188.                     else begin
  2189.                             repeat
  2190.                                 h := h + 1;
  2191.                             until PixelInside(h, v) or (h >= PicRect.right);
  2192.                             direction := 'U';
  2193.                             if h >= PicRect.right then begin
  2194.                                     if not macro then
  2195.                                         beep;
  2196.                                     exit(AutoOutline);
  2197.                                 end;
  2198.                         end;
  2199.                 if TraceEdge(start.h, start.v, direction, TouchingEdge) then begin
  2200.                     if GetHandleSize(handle(roiRgn)) = 10 then
  2201.                         roiType := RectRoi;
  2202.                     WhatToUndo := NothingToUndo;
  2203.                     if WandAutoMeasure and not MakingLOI then begin
  2204.                             GetHistogram;
  2205.                             ComputeResults;
  2206.                             if WandAdjustAreas then begin
  2207.                                     GetLengthOrPerimeter(Perimeter, CalibratedPerimeter);
  2208.                                     with RoiRect do
  2209.                                         AspectRatio := (right - left) / (bottom - top);
  2210.                                     count := PixelCount^[mCount] + round(Perimeter / 2.0 + AspectRatio * 1.5);
  2211.                                     PixelCount^[mCount] := count;
  2212.                                     if SpatiallyCalibrated then
  2213.                                         mArea^[mCount] := count / (xScale * yScale)
  2214.                                     else
  2215.                                         mArea^[mCount] := count;
  2216.                                 end;
  2217.                             ShowInfo;
  2218.                             AppendResults;
  2219.                             WhatToUndo := UndoMeasurement;
  2220.                             if LabelParticles then
  2221.                                 MarkSelection(mCount);
  2222.                         end;
  2223.                     if not (WandAutoMeasure and LabelParticles) then
  2224.                         RoiShowing := true;
  2225.                     if not MakingLOI then
  2226.                         UpdateScreen(RoiRect);
  2227.                     if not WandAutoMeasure then
  2228.                         measuring := false;
  2229.                 end; {if}
  2230.             end; {with info}
  2231.     end;
  2232.  
  2233.  
  2234.     procedure RedoMeasurement;
  2235.         var
  2236.             SaveN, temp: integer;
  2237.             Canceled: boolean;
  2238.     begin
  2239.         if not isSelectionTool then begin
  2240.                 CurrentTool := SelectionTool;
  2241.                 isSelectionTool := true;
  2242.                 DrawTools;
  2243.             end;
  2244.         temp := GetInt('Measurement to Redo:', mCount, Canceled);
  2245.         if canceled then
  2246.             exit(RedoMeasurement);
  2247.         MeasurementToRedo := temp;
  2248.         if (MeasurementToRedo >= 1) and (MeasurementToRedo <= mCount) then begin
  2249.                 SaveN := mCount;
  2250.                 mCount := MeasurementToRedo;
  2251.                 ShowInfo;
  2252.                 mCount := SaveN;
  2253.             end
  2254.         else begin
  2255.                 beep;
  2256.                 MeasurementToRedo := 0;
  2257.             end;
  2258.     end;
  2259.  
  2260.  
  2261.     procedure DeleteMeasurement;
  2262.         var
  2263.             nToDelete, i: integer;
  2264.             Canceled: boolean;
  2265.     begin
  2266.         nToDelete := GetInt('Measurement to delete:', mCount, Canceled);
  2267.         if (nToDelete >= 1) and (nToDelete <= mCount) and not Canceled then begin
  2268.                 for i := nToDelete to mCount - 1 do begin
  2269.                         mean^[i] := mean^[i + 1];
  2270.                         sd^[i] := sd^[i + 1];
  2271.                         PixelCount^[i] := PixelCount^[i + 1];
  2272.                         mArea^[i] := mArea^[i + 1];
  2273.                         mode^[i] := mode^[i + 1];
  2274.                         IntegratedDensity^[i] := IntegratedDensity^[i + 1];
  2275.                         idBackground^[i] := idBackground^[i + 1];
  2276.                         xcenter^[i] := xcenter^[i + 1];
  2277.                         ycenter^[i] := ycenter^[i + 1];
  2278.                         MajorAxis^[i] := MajorAxis^[i + 1];
  2279.                         MinorAxis^[i] := MinorAxis^[i + 1];
  2280.                         orientation^[i] := orientation^[i + 1];
  2281.                         mMin^[i] := mMin^[i + 1];
  2282.                         mMax^[i] := mMax^[i + 1];
  2283.                         plength^[i] := plength^[i + 1];
  2284.                     end; {for}
  2285.                 mCount := mCount - 1;
  2286.                 if mCount = 0 then begin
  2287.                         UnsavedResults := false;
  2288.                         beep;
  2289.                     end;
  2290.                 UpdateList;
  2291.             end
  2292.         else if not Canceled then
  2293.             beep;
  2294.     end;
  2295.  
  2296.  
  2297.     function DoAPDialog: boolean;
  2298.         const
  2299.             MinID = 6;
  2300.             MaxID = 7;
  2301.             LabelID = 8;
  2302.             OutlineID = 9;
  2303.             IgnoreID = 10;
  2304.             IncludeHolesID = 11;
  2305.             ResetID = 12;
  2306.         var
  2307.             mylog: DialogPtr;
  2308.             item: integer;
  2309.     begin
  2310.         InitCursor;
  2311.         mylog := GetNewDialog(220, nil, pointer(-1));
  2312.         SetDNum(MyLog, MinID, MinParticleSize);
  2313.         SetDNum(MyLog, MaxID, MaxParticleSize);
  2314.         SetDlogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2315.         SetDlogItem(mylog, LabelID, ord(LabelParticles));
  2316.         SetDlogItem(mylog, OutlineID, ord(OutlineParticles));
  2317.         SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2318.         SetDlogItem(mylog, ResetID, ord(APReset));
  2319.         repeat
  2320.             ModalDialog(nil, item);
  2321.             if item = MinID then
  2322.                 MinParticleSize := GetDNum(MyLog, MinID);
  2323.             if item = MaxID then
  2324.                 MaxParticleSize := GetDNum(MyLog, MaxID);
  2325.             if item = IgnoreID then begin
  2326.                     IgnoreParticlesTouchingEdge := not IgnoreParticlesTouchingEdge;
  2327.                     SetDlogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2328.                 end;
  2329.             if item = LabelID then begin
  2330.                     LabelParticles := not LabelParticles;
  2331.                     SetDlogItem(mylog, LabelID, ord(LabelParticles));
  2332.                 end;
  2333.             if item = OutlineID then begin
  2334.                     OutlineParticles := not OutlineParticles;
  2335.                     SetDlogItem(mylog, OutlineID, ord(OutlineParticles));
  2336.                 end;
  2337.             if item = IncludeHolesID then begin
  2338.                     IncludeHoles := not IncludeHoles;
  2339.                     SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2340.                 end;
  2341.             if item = ResetID then begin
  2342.                     APReset := not APReset;
  2343.                     SetDlogItem(mylog, ResetID, ord(APReset));
  2344.                 end;
  2345.         until (item = ok) or (item = cancel);
  2346.         DisposeDialog(mylog);
  2347.         if MinParticleSize < 1 then
  2348.             MinParticleSize := 1;
  2349.         if MaxParticleSize > 9999999 then
  2350.             MaxParticleSize := 9999999;
  2351.         if MaxParticleSize <= MinParticleSize then begin
  2352.                 MinParticleSize := 1;
  2353.                 MaxParticleSize := 999999;
  2354.             end;
  2355.         DoAPDialog := item <> cancel;
  2356.     end;
  2357.  
  2358.  
  2359.     procedure AnalyzeParticles;
  2360.     {
  2361.     Here's how it works:      (thanks to Stein Roervik)
  2362.         for each line do
  2363.               for each pixel in this line do
  2364.                 if the pixel value is "inside" the threshold range then
  2365.                   trace the edge to mark the object
  2366.                   do the measurement
  2367.                   fill the object with a colour that is outside the threshold range
  2368.                 else
  2369.                   continue the scan
  2370.     }
  2371.         var
  2372.             hloc, vloc, AlertID, index, MaxTriesPerLine, nParticles: integer;
  2373.             SaveSliceState, TouchingEdge, DrawOutlines, AutoSelectAll, finished, OutsideSelection: boolean;
  2374.             SaveForegroundIndex, SaveBackgroundIndex, EraseIndex, OutlineIndex: integer;
  2375.             savePort: GrafPtr;
  2376.             ScanRect: rect;
  2377.             side: (TopSide, RightSide, BottomSide, LeftSide);
  2378.             dstRgn: rgnHandle;
  2379.             StartCount: integer;
  2380.             SaveGDevice: GDHandle;
  2381.  
  2382.         function PixelInside: boolean;
  2383.             var
  2384.                 value: integer;
  2385.                 offset: LongInt;
  2386.                 p: ptr;
  2387.         begin
  2388.             with info^ do begin {MyGetPixel inlined to speed things up.}
  2389.                     offset := vloc * BytesPerRow + hloc;
  2390.                     p := ptr(ord4(PicBaseAddr) + offset);
  2391.                 end;
  2392.             value := BAND(p^, 255);
  2393.             case ThresholdingMode of
  2394.                 DensitySlice: 
  2395.                     PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  2396.                 GrayMapThresholding: 
  2397.                     PixelInside := value >= GrayMapThreshold;
  2398.                 BinaryImage: 
  2399.                     PixelInside := value = BlackIndex;
  2400.             end;
  2401.         end;
  2402.  
  2403.         procedure LabelBlobs;
  2404.             var
  2405.                 i,j: integer;
  2406.         begin
  2407.             j := 0;
  2408.             if (StartCount - 1 + nParticles) <= MaxMeasurements then
  2409.                 for i := StartCount to mCount do begin
  2410.                         MarkSelection(i);
  2411.                         j := j + 1;
  2412.                         if j mod 50 = 0 then
  2413.                             UpdatePicWindow;
  2414.                         if CommandPeriod then begin
  2415.                                 beep;
  2416.                                 leave;
  2417.                             end;
  2418.                     end;
  2419.         end;
  2420.         
  2421.  
  2422.         procedure abort;
  2423.         begin
  2424.             SetGDevice(SaveGDevice);
  2425.             SetPort(SavePort);
  2426.             if LabelParticles then
  2427.                 LabelBlobs;
  2428.             DensitySlicing := SaveSliceState;
  2429.             SetForegroundColor(SaveForegroundIndex);
  2430.             SetBackgroundColor(SaveBackgroundIndex);
  2431.             KillRoi;
  2432.             UpdatePicWindow;
  2433.             WhatToUndo := UndoEdit;
  2434.             UndoFromClip := true;
  2435.             AnalyzingParticles := false;
  2436.             DisposeRgn(dstRgn);
  2437.         end;
  2438.  
  2439.  
  2440.     begin
  2441.         with info^ do begin
  2442.                 if NotInBounds or NoUndo then
  2443.                     exit(AnalyzeParticles);
  2444.                 if not SetupAutoOutline(false) then
  2445.                     exit(AnalyzeParticles);
  2446.                 if not macro and not OptionKeyWasDown then
  2447.                     if not DoAPDialog then
  2448.                         exit(AnalyzeParticles);
  2449.                 AutoSelectAll := not RoiShowing;
  2450.                 if AutoSelectAll then
  2451.                     SelectAll(false);
  2452.                 ScanRect := RoiRect;
  2453.                 if not AutoSelectAll then
  2454.                     with ScanRect do begin
  2455.                             left := picrect.left;
  2456.                             right := PicRect.right;
  2457.                         end;
  2458.                 KillRoi;
  2459.                 if APReset then begin
  2460.                         ResetCounter;
  2461.                         if mCount > 0 then
  2462.                             exit(AnalyzeParticles);
  2463.                     end;
  2464.                 StartCount := mCount + 1;
  2465.                 UpdatePicWindow;
  2466.                 SetupUndoFromClip;
  2467.                 SaveSliceState := DensitySlicing;
  2468.                 SaveForegroundIndex := ForegroundIndex;
  2469.                 SaveBackgroundIndex := BackgroundIndex;
  2470.                 SetForegroundColor(WhiteIndex);
  2471.                 DensitySlicing := false;
  2472.                 DrawOutlines := false;
  2473.                 case ThresholdingMode of
  2474.                     DensitySlice:  begin
  2475.                             EraseIndex := SliceStart - 1;
  2476.                             if EraseIndex < 0 then
  2477.                                 EraseIndex := WhiteIndex;
  2478.                             DrawOutlines := OutlineParticles;
  2479.                             OutLineIndex := BlackIndex;
  2480.                         end;
  2481.                     GrayMapThresholding:  begin
  2482.                             EraseIndex := GrayMapThreshold - 1;
  2483.                             if EraseIndex < 0 then
  2484.                                 EraseIndex := WhiteIndex;
  2485.                         end;
  2486.                     BinaryImage:  begin
  2487.                             DrawOutlines := OutlineParticles;
  2488.                             OutLineIndex := 254;
  2489.                             EraseIndex := 128;
  2490.                         end;
  2491.                 end;
  2492.                 AnalyzingParticles := true;
  2493.                 nParticles := 0;
  2494.                 SaveGDevice := GetGDevice;
  2495.                 SetGDevice(osGDevice);
  2496.                 GetPort(SavePort);
  2497.                 SetPort(GrafPtr(osPort));
  2498.                 dstRgn := NewRgn;
  2499.                 SelectionMode := NewSelection;
  2500.                 ShowWatch;
  2501.                 with ScanRect do
  2502.                     for vloc := top to bottom - 1 do
  2503.                         for hloc := left to right - 1 do begin
  2504.                                 if PixelInside then begin
  2505.                                         if TraceEdge(hloc, vloc, 'U', TouchingEdge) then begin
  2506.                                                 nParticles := nParticles + 1;
  2507.                                                 RoiShowing := false;
  2508.                                                 if mCount < MaxMeasurements then begin
  2509.                                                         GetHistogram;
  2510.                                                         ComputeResults;
  2511.                                                     end;
  2512.                                                 SetBackgroundColor(EraseIndex);
  2513.                                                 EraseRgn(roiRgn);
  2514.                                                 if AutoSelectAll then
  2515.                                                     OutSideSelection := false
  2516.                                                 else begin
  2517.                                                         SectRgn(roiRgn, NoInfo^.RoiRgn, dstRgn);
  2518.                                                         OutSideSelection := EmptyRgn(dstRgn);
  2519.                                                     end;
  2520.                                                 if (PixelCount^[mCount] < MinParticleSize) or (PixelCount^[mCount] > MaxParticleSize) or TouchingEdge or OutsideSelection then begin
  2521.                                                         mCount := mCount - 1;
  2522.                                                         nParticles := nParticles - 1;
  2523.                                                         UpdateScreen(RoiRect);
  2524.                                                         if AnalyzingParticles = false then begin
  2525.                                                                 abort;
  2526.                                                                 exit(AnalyzeParticles);
  2527.                                                             end;
  2528.                                                     end
  2529.                                                 else begin
  2530.                                                         if DrawOutlines then begin
  2531.                                                                 SetForegroundColor(OutlineIndex);
  2532.                                                                 FrameRgn(roiRgn);
  2533.                                                             end;
  2534.                                                         UpdateScreen(RoiRect);
  2535.                                                         if nParticles <= MaxMeasurements then
  2536.                                                             AppendResults;
  2537.                                                         if (nParticles mod 10) = 0 then ShowMessage(long2str(nParticles));
  2538.                                                         if nParticles = MaxMeasurements then
  2539.                                                             beep;
  2540.                                                         if CommandPeriod or (AnalyzingParticles = false) then begin {quit}
  2541.                                                                 beep;
  2542.                                                                 abort;
  2543.                                                                 exit(AnalyzeParticles);
  2544.                                                             end; {quit}
  2545.                                                     end;
  2546.                                             end   {if TraceEdge}
  2547.                                             else begin
  2548.                                                 abort; {perimeter too large}
  2549.                                                 exit(AnalyzeParticles);
  2550.                                             end;
  2551.                                     end; {if PixelInside}
  2552.                             end; {for}
  2553.             end; {with}
  2554.         ShowMessage(StringOf('Count=',nParticles:1));
  2555.         SetGDevice(SaveGDevice);
  2556.         SetPort(SavePort);
  2557.         if LabelParticles then
  2558.             LabelBlobs;
  2559.         DensitySlicing := SaveSliceState;
  2560.         SetForegroundColor(SaveForegroundIndex);
  2561.         SetBackgroundColor(SaveBackgroundIndex);
  2562.         KillRoi;
  2563.         UpdatePicWindow;
  2564.         if ThresholdingMode = GrayMapThresholding then
  2565.             ResetGrayMap;
  2566.         WhatToUndo := UndoEdit;
  2567.         UndoFromClip := true;
  2568.         AnalyzingParticles := false;
  2569.         DisposeRgn(dstRgn);
  2570.     end;
  2571.  
  2572.  
  2573.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  2574.         var
  2575.             i, ff: integer;
  2576.             SaveInfo: InfoPtr;
  2577.             pt, spt, start: point;
  2578.             SaveGDevice: GDHandle;
  2579.     begin
  2580.         SetupUndoInfoRec;
  2581.         SaveInfo := Info;
  2582.         Info := UndoInfo;
  2583.         SaveGDevice := GetGDevice;
  2584.         SetGDevice(osGDevice);
  2585.         with info^ do begin
  2586.                 magnification := SaveInfo^.magnification;
  2587.                 SrcRect := SaveInfo^.SrcRect;
  2588.                 BinaryPic := true;
  2589.                 SetPort(GrafPtr(osPort));
  2590.             end;
  2591.         pmForeColor(BlackIndex);
  2592.         pmBackColor(WhiteIndex);
  2593.         PenNormal;
  2594.         PenSize(LineWidth, LineWidth);
  2595.         EraseRect(info^.PicRect);
  2596.         ff := LineWidth div 2;
  2597.         if ff < 0 then
  2598.             ff := 0;
  2599.         MakingLOI := true;
  2600.         ConvertCoordinates;
  2601.         spt.h := xCoordinates^[1];
  2602.         spt.v := yCoordinates^[1];
  2603.         MoveTo(spt.h - ff, spt.v - ff);
  2604.         for i := 2 to nCoordinates do begin
  2605.                 pt.h := xCoordinates^[i];
  2606.                 pt.v := yCoordinates^[i];
  2607.                 LineTo(pt.h - ff, pt.v - ff);
  2608.             end;
  2609.         start := spt;
  2610.         start.h := start.h - 1;
  2611.         AutoOutline(start);
  2612.         MakingLOI := false;
  2613.         info^.RoiShowing := false;
  2614.         Info := SaveInfo;
  2615.         SetGDevice(SaveGDevice);
  2616.         with info^ do begin
  2617.                 CopyRgn(UndoInfo^.roiRgn, roiRgn);
  2618.                 RoiRect := UndoInfo^.RoiRect;
  2619.                 SetEmptyRgn(UndoInfo^.roiRgn);
  2620.                 RoiShowing := true;
  2621.                 SetupUndo;
  2622.                 roiType := RoiKind;
  2623.                 with RoiRect do begin
  2624.                         LX1 := spt.h - left;
  2625.                         LY1 := spt.v - top;
  2626.                         LX2 := pt.h - left;
  2627.                         LY2 := pt.v - top;
  2628.                     end;
  2629.             end; {with info^}
  2630.         MakeCoordinatesRelative;
  2631.     end;
  2632.  
  2633.  
  2634. end.